From 7a2e20caac9db6f789a7b3fab344b9758af45335 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 19 Apr 2015 21:02:06 -0700 Subject: j3dcore: flatten the directory structure a bit Signed-off-by: Harvey Harrison --- src/javax/media/j3d/Alpha.java | 1022 ++ src/javax/media/j3d/AlternateAppearance.java | 619 + .../media/j3d/AlternateAppearanceRetained.java | 866 ++ src/javax/media/j3d/AmbientLight.java | 106 + src/javax/media/j3d/AmbientLightRetained.java | 52 + src/javax/media/j3d/Appearance.java | 1032 ++ src/javax/media/j3d/AppearanceRetained.java | 1413 +++ src/javax/media/j3d/AssertionFailureException.java | 49 + src/javax/media/j3d/AttributeBin.java | 418 + src/javax/media/j3d/AudioDevice.java | 271 + src/javax/media/j3d/AudioDevice3D.java | 533 + src/javax/media/j3d/AudioDevice3DL2.java | 319 + src/javax/media/j3d/AudioDeviceEnumerator.java | 83 + src/javax/media/j3d/AuralAttributes.java | 1307 +++ src/javax/media/j3d/AuralAttributesRetained.java | 667 ++ src/javax/media/j3d/AutoOffScreenCanvas3D.java | 21 + src/javax/media/j3d/BHInsertStructure.java | 154 + src/javax/media/j3d/BHInternalNode.java | 209 + src/javax/media/j3d/BHLeafInterface.java | 40 + src/javax/media/j3d/BHLeafNode.java | 107 + src/javax/media/j3d/BHNode.java | 281 + src/javax/media/j3d/BHTree.java | 1154 ++ src/javax/media/j3d/Background.java | 789 ++ src/javax/media/j3d/BackgroundRetained.java | 782 ++ src/javax/media/j3d/BackgroundSound.java | 153 + src/javax/media/j3d/BackgroundSoundRetained.java | 40 + src/javax/media/j3d/BadTransformException.java | 69 + src/javax/media/j3d/Behavior.java | 525 + src/javax/media/j3d/BehaviorRetained.java | 525 + src/javax/media/j3d/BehaviorScheduler.java | 244 + src/javax/media/j3d/BehaviorStructure.java | 1675 +++ src/javax/media/j3d/Billboard.java | 675 ++ src/javax/media/j3d/BoundingBox.java | 1904 ++++ src/javax/media/j3d/BoundingLeaf.java | 186 + src/javax/media/j3d/BoundingLeafRetained.java | 285 + src/javax/media/j3d/BoundingPolytope.java | 1783 +++ src/javax/media/j3d/BoundingSphere.java | 1737 +++ src/javax/media/j3d/Bounds.java | 673 ++ src/javax/media/j3d/BranchGroup.java | 580 + src/javax/media/j3d/BranchGroupRetained.java | 377 + src/javax/media/j3d/CachedFrustum.java | 234 + src/javax/media/j3d/CachedTargets.java | 122 + src/javax/media/j3d/Canvas3D.java | 4951 ++++++++ src/javax/media/j3d/CanvasViewCache.java | 2044 ++++ src/javax/media/j3d/CanvasViewEventCatcher.java | 99 + src/javax/media/j3d/CapabilityBits.java | 541 + src/javax/media/j3d/CapabilityNotSetException.java | 51 + src/javax/media/j3d/Clip.java | 313 + src/javax/media/j3d/ClipRetained.java | 400 + src/javax/media/j3d/ColorInterpolator.java | 314 + src/javax/media/j3d/ColoringAttributes.java | 370 + .../media/j3d/ColoringAttributesRetained.java | 265 + src/javax/media/j3d/CompileState.java | 310 + src/javax/media/j3d/CompressedGeometry.java | 428 + src/javax/media/j3d/CompressedGeometryHeader.java | 256 + .../media/j3d/CompressedGeometryRenderMethod.java | 112 + .../media/j3d/CompressedGeometryRetained.java | 470 + src/javax/media/j3d/ConeSound.java | 960 ++ src/javax/media/j3d/ConeSoundRetained.java | 656 ++ src/javax/media/j3d/Context.java | 36 + .../media/j3d/DanglingReferenceException.java | 58 + src/javax/media/j3d/DecalGroup.java | 94 + src/javax/media/j3d/DecalGroupRetained.java | 34 + src/javax/media/j3d/DefaultRenderMethod.java | 83 + src/javax/media/j3d/DepthComponent.java | 89 + src/javax/media/j3d/DepthComponentFloat.java | 134 + .../media/j3d/DepthComponentFloatRetained.java | 88 + src/javax/media/j3d/DepthComponentInt.java | 132 + src/javax/media/j3d/DepthComponentIntRetained.java | 88 + src/javax/media/j3d/DepthComponentNative.java | 124 + .../media/j3d/DepthComponentNativeRetained.java | 77 + src/javax/media/j3d/DepthComponentRetained.java | 62 + src/javax/media/j3d/DirectionalLight.java | 220 + src/javax/media/j3d/DirectionalLightRetained.java | 218 + src/javax/media/j3d/DisplayListRenderMethod.java | 269 + src/javax/media/j3d/DistanceLOD.java | 310 + src/javax/media/j3d/Drawable.java | 36 + src/javax/media/j3d/DrawingSurfaceObject.java | 56 + src/javax/media/j3d/EnvironmentSet.java | 549 + src/javax/media/j3d/EventCatcher.java | 433 + src/javax/media/j3d/ExceptionStrings.properties | 1003 ++ src/javax/media/j3d/ExponentialFog.java | 228 + src/javax/media/j3d/ExponentialFogRetained.java | 183 + src/javax/media/j3d/Fog.java | 568 + src/javax/media/j3d/FogRetained.java | 815 ++ src/javax/media/j3d/Font3D.java | 1184 ++ src/javax/media/j3d/FontExtrusion.java | 255 + src/javax/media/j3d/FreeListManager.java | 84 + src/javax/media/j3d/GLSLShaderProgram.java | 179 + src/javax/media/j3d/GLSLShaderProgramRetained.java | 429 + src/javax/media/j3d/GeneralizedStrip.java | 889 ++ src/javax/media/j3d/GeneralizedStripFlags.java | 87 + src/javax/media/j3d/GeneralizedVertexList.java | 406 + src/javax/media/j3d/Geometry.java | 67 + src/javax/media/j3d/GeometryArray.java | 7467 ++++++++++++ src/javax/media/j3d/GeometryArrayRetained.java | 11213 +++++++++++++++++++ src/javax/media/j3d/GeometryAtom.java | 263 + src/javax/media/j3d/GeometryDecompressor.java | 1217 ++ .../media/j3d/GeometryDecompressorRetained.java | 404 + .../media/j3d/GeometryDecompressorShape3D.java | 487 + src/javax/media/j3d/GeometryLock.java | 85 + src/javax/media/j3d/GeometryRetained.java | 387 + src/javax/media/j3d/GeometryStripArray.java | 268 + .../media/j3d/GeometryStripArrayRetained.java | 828 ++ src/javax/media/j3d/GeometryStructure.java | 1190 ++ src/javax/media/j3d/GeometryUpdater.java | 57 + .../media/j3d/GraphStructureChangeListener.java | 68 + src/javax/media/j3d/GraphicsConfigInfo.java | 58 + src/javax/media/j3d/GraphicsConfigTemplate3D.java | 446 + src/javax/media/j3d/GraphicsContext3D.java | 3068 +++++ src/javax/media/j3d/Group.java | 555 + src/javax/media/j3d/GroupRetained.java | 3207 ++++++ src/javax/media/j3d/HashKey.java | 257 + src/javax/media/j3d/HiResCoord.java | 738 ++ .../media/j3d/IllegalRenderingStateException.java | 49 + .../media/j3d/IllegalSceneGraphException.java | 54 + src/javax/media/j3d/IllegalSharingException.java | 74 + src/javax/media/j3d/ImageComponent.java | 434 + src/javax/media/j3d/ImageComponent2D.java | 743 ++ src/javax/media/j3d/ImageComponent2DRetained.java | 337 + src/javax/media/j3d/ImageComponent3D.java | 979 ++ src/javax/media/j3d/ImageComponent3DRetained.java | 382 + src/javax/media/j3d/ImageComponentRetained.java | 2653 +++++ src/javax/media/j3d/ImageComponentUpdateInfo.java | 44 + src/javax/media/j3d/IndexedGeometryArray.java | 1278 +++ .../media/j3d/IndexedGeometryArrayRetained.java | 1932 ++++ src/javax/media/j3d/IndexedGeometryStripArray.java | 261 + .../j3d/IndexedGeometryStripArrayRetained.java | 247 + src/javax/media/j3d/IndexedLineArray.java | 219 + src/javax/media/j3d/IndexedLineArrayRetained.java | 431 + src/javax/media/j3d/IndexedLineStripArray.java | 247 + .../media/j3d/IndexedLineStripArrayRetained.java | 494 + src/javax/media/j3d/IndexedObject.java | 68 + src/javax/media/j3d/IndexedPointArray.java | 218 + src/javax/media/j3d/IndexedPointArrayRetained.java | 309 + src/javax/media/j3d/IndexedQuadArray.java | 222 + src/javax/media/j3d/IndexedQuadArrayRetained.java | 459 + src/javax/media/j3d/IndexedTriangleArray.java | 222 + .../media/j3d/IndexedTriangleArrayRetained.java | 439 + src/javax/media/j3d/IndexedTriangleFanArray.java | 247 + .../media/j3d/IndexedTriangleFanArrayRetained.java | 512 + src/javax/media/j3d/IndexedTriangleStripArray.java | 248 + .../j3d/IndexedTriangleStripArrayRetained.java | 533 + src/javax/media/j3d/IndexedUnorderSet.java | 613 + src/javax/media/j3d/InputDevice.java | 167 + src/javax/media/j3d/InputDeviceBlockingThread.java | 120 + src/javax/media/j3d/InputDeviceScheduler.java | 214 + src/javax/media/j3d/IntegerFreeList.java | 56 + src/javax/media/j3d/Interpolator.java | 140 + src/javax/media/j3d/J3DBuffer.java | 220 + src/javax/media/j3d/J3DGraphics2D.java | 192 + src/javax/media/j3d/J3DGraphics2DImpl.java | 1214 ++ src/javax/media/j3d/J3dClock.java | 96 + src/javax/media/j3d/J3dDebug.java | 453 + src/javax/media/j3d/J3dHash.java | 44 + src/javax/media/j3d/J3dI18N.java | 45 + src/javax/media/j3d/J3dMessage.java | 185 + src/javax/media/j3d/J3dNotification.java | 58 + src/javax/media/j3d/J3dQueryProps.java | 138 + src/javax/media/j3d/J3dStructure.java | 165 + src/javax/media/j3d/J3dThread.java | 354 + src/javax/media/j3d/J3dThreadData.java | 106 + src/javax/media/j3d/JoglContext.java | 109 + src/javax/media/j3d/JoglDrawable.java | 63 + src/javax/media/j3d/JoglDrawingSurfaceObject.java | 78 + src/javax/media/j3d/JoglGraphicsConfiguration.java | 145 + src/javax/media/j3d/JoglPipeline.java | 8963 +++++++++++++++ src/javax/media/j3d/JoglShaderObject.java | 39 + src/javax/media/j3d/LOD.java | 236 + src/javax/media/j3d/Leaf.java | 48 + src/javax/media/j3d/LeafRetained.java | 60 + src/javax/media/j3d/Light.java | 729 ++ src/javax/media/j3d/LightBin.java | 464 + src/javax/media/j3d/LightRetained.java | 1074 ++ src/javax/media/j3d/LightSet.java | 102 + src/javax/media/j3d/LineArray.java | 194 + src/javax/media/j3d/LineArrayRetained.java | 456 + src/javax/media/j3d/LineAttributes.java | 500 + src/javax/media/j3d/LineAttributesRetained.java | 344 + src/javax/media/j3d/LineStripArray.java | 217 + src/javax/media/j3d/LineStripArrayRetained.java | 556 + src/javax/media/j3d/LinearFog.java | 272 + src/javax/media/j3d/LinearFogRetained.java | 200 + src/javax/media/j3d/Link.java | 167 + src/javax/media/j3d/LinkRetained.java | 353 + src/javax/media/j3d/Locale.java | 1088 ++ src/javax/media/j3d/MRSWLock.java | 88 + src/javax/media/j3d/MasterControl.java | 3748 +++++++ src/javax/media/j3d/MasterControlThread.java | 76 + src/javax/media/j3d/Material.java | 717 ++ src/javax/media/j3d/MaterialRetained.java | 564 + src/javax/media/j3d/MediaContainer.java | 354 + src/javax/media/j3d/MediaContainerRetained.java | 206 + src/javax/media/j3d/MemoryFreeList.java | 277 + src/javax/media/j3d/ModelClip.java | 737 ++ src/javax/media/j3d/ModelClipRetained.java | 1043 ++ src/javax/media/j3d/Morph.java | 705 ++ src/javax/media/j3d/MorphRetained.java | 1975 ++++ src/javax/media/j3d/MultipleParentException.java | 51 + src/javax/media/j3d/NioImageBuffer.java | 393 + src/javax/media/j3d/NnuId.java | 39 + src/javax/media/j3d/NnuIdManager.java | 357 + src/javax/media/j3d/Node.java | 823 ++ src/javax/media/j3d/NodeComponent.java | 308 + src/javax/media/j3d/NodeComponentRetained.java | 248 + src/javax/media/j3d/NodeComponentUpdate.java | 40 + src/javax/media/j3d/NodeData.java | 35 + src/javax/media/j3d/NodeReferenceTable.java | 135 + src/javax/media/j3d/NodeRetained.java | 983 ++ src/javax/media/j3d/NoopDrawingSurfaceObject.java | 75 + src/javax/media/j3d/NoopPipeline.java | 1448 +++ src/javax/media/j3d/NotificationThread.java | 139 + src/javax/media/j3d/ObjectUpdate.java | 40 + src/javax/media/j3d/OrderedBin.java | 123 + src/javax/media/j3d/OrderedChildInfo.java | 78 + src/javax/media/j3d/OrderedCollection.java | 73 + src/javax/media/j3d/OrderedGroup.java | 486 + src/javax/media/j3d/OrderedGroupRetained.java | 527 + src/javax/media/j3d/OrderedPath.java | 52 + src/javax/media/j3d/OrderedPathElement.java | 38 + src/javax/media/j3d/OrientedShape3D.java | 692 ++ .../media/j3d/OrientedShape3DRenderMethod.java | 115 + src/javax/media/j3d/OrientedShape3DRetained.java | 610 + src/javax/media/j3d/PathInterpolator.java | 292 + src/javax/media/j3d/PhysicalBody.java | 368 + src/javax/media/j3d/PhysicalEnvironment.java | 525 + src/javax/media/j3d/PickBounds.java | 108 + src/javax/media/j3d/PickCone.java | 108 + src/javax/media/j3d/PickConeRay.java | 280 + src/javax/media/j3d/PickConeSegment.java | 306 + src/javax/media/j3d/PickCylinder.java | 117 + src/javax/media/j3d/PickCylinderRay.java | 262 + src/javax/media/j3d/PickCylinderSegment.java | 275 + src/javax/media/j3d/PickInfo.java | 1093 ++ src/javax/media/j3d/PickPoint.java | 117 + src/javax/media/j3d/PickRay.java | 139 + src/javax/media/j3d/PickSegment.java | 125 + src/javax/media/j3d/PickShape.java | 85 + src/javax/media/j3d/Pipeline.java | 1182 ++ src/javax/media/j3d/PointArray.java | 186 + src/javax/media/j3d/PointArrayRetained.java | 308 + src/javax/media/j3d/PointAttributes.java | 236 + src/javax/media/j3d/PointAttributesRetained.java | 221 + src/javax/media/j3d/PointLight.java | 325 + src/javax/media/j3d/PointLightRetained.java | 339 + src/javax/media/j3d/PointSound.java | 580 + src/javax/media/j3d/PointSoundRetained.java | 306 + src/javax/media/j3d/PolygonAttributes.java | 494 + src/javax/media/j3d/PolygonAttributesRetained.java | 360 + src/javax/media/j3d/PositionInterpolator.java | 220 + src/javax/media/j3d/PositionPathInterpolator.java | 279 + src/javax/media/j3d/QuadArray.java | 193 + src/javax/media/j3d/QuadArrayRetained.java | 525 + src/javax/media/j3d/Raster.java | 822 ++ src/javax/media/j3d/RasterRetained.java | 691 ++ src/javax/media/j3d/RenderAtom.java | 369 + src/javax/media/j3d/RenderAtomListInfo.java | 60 + src/javax/media/j3d/RenderBin.java | 7066 ++++++++++++ src/javax/media/j3d/RenderMethod.java | 41 + src/javax/media/j3d/RenderMolecule.java | 3039 +++++ src/javax/media/j3d/Renderer.java | 1751 +++ src/javax/media/j3d/RendererStructure.java | 71 + src/javax/media/j3d/RenderingAttributes.java | 1501 +++ .../media/j3d/RenderingAttributesRetained.java | 729 ++ .../media/j3d/RenderingAttributesStructure.java | 248 + .../media/j3d/RenderingEnvironmentStructure.java | 1746 +++ src/javax/media/j3d/RenderingError.java | 268 + src/javax/media/j3d/RenderingErrorListener.java | 43 + src/javax/media/j3d/RestrictedAccessException.java | 51 + src/javax/media/j3d/RotPosPathInterpolator.java | 394 + .../media/j3d/RotPosScalePathInterpolator.java | 466 + src/javax/media/j3d/RotationInterpolator.java | 218 + src/javax/media/j3d/RotationPathInterpolator.java | 312 + src/javax/media/j3d/ScaleInterpolator.java | 221 + src/javax/media/j3d/SceneGraphCycleException.java | 58 + src/javax/media/j3d/SceneGraphObject.java | 513 + src/javax/media/j3d/SceneGraphObjectRetained.java | 184 + src/javax/media/j3d/SceneGraphPath.java | 668 ++ src/javax/media/j3d/Screen3D.java | 468 + src/javax/media/j3d/ScreenViewCache.java | 134 + src/javax/media/j3d/Sensor.java | 544 + src/javax/media/j3d/SensorRead.java | 180 + src/javax/media/j3d/SetLiveState.java | 270 + src/javax/media/j3d/Shader.java | 145 + src/javax/media/j3d/ShaderAppearance.java | 304 + src/javax/media/j3d/ShaderAppearanceRetained.java | 397 + src/javax/media/j3d/ShaderAttrLoc.java | 36 + src/javax/media/j3d/ShaderAttribute.java | 90 + src/javax/media/j3d/ShaderAttributeArray.java | 163 + .../media/j3d/ShaderAttributeArrayRetained.java | 1057 ++ src/javax/media/j3d/ShaderAttributeBinding.java | 142 + .../media/j3d/ShaderAttributeBindingRetained.java | 70 + src/javax/media/j3d/ShaderAttributeObject.java | 143 + .../media/j3d/ShaderAttributeObjectRetained.java | 332 + src/javax/media/j3d/ShaderAttributeRetained.java | 66 + src/javax/media/j3d/ShaderAttributeSet.java | 274 + .../media/j3d/ShaderAttributeSetRetained.java | 391 + src/javax/media/j3d/ShaderAttributeValue.java | 116 + .../media/j3d/ShaderAttributeValueRetained.java | 537 + src/javax/media/j3d/ShaderBin.java | 367 + src/javax/media/j3d/ShaderConstants.java | 48 + src/javax/media/j3d/ShaderError.java | 425 + src/javax/media/j3d/ShaderErrorListener.java | 47 + src/javax/media/j3d/ShaderId.java | 36 + src/javax/media/j3d/ShaderProgram.java | 209 + src/javax/media/j3d/ShaderProgramId.java | 36 + src/javax/media/j3d/ShaderProgramRetained.java | 1233 ++ src/javax/media/j3d/ShaderRetained.java | 170 + src/javax/media/j3d/Shape3D.java | 794 ++ src/javax/media/j3d/Shape3DCompileRetained.java | 395 + src/javax/media/j3d/Shape3DRetained.java | 2844 +++++ src/javax/media/j3d/SharedGroup.java | 168 + src/javax/media/j3d/SharedGroupRetained.java | 931 ++ src/javax/media/j3d/Sound.java | 1197 ++ src/javax/media/j3d/SoundException.java | 49 + src/javax/media/j3d/SoundRetained.java | 1316 +++ src/javax/media/j3d/SoundScheduler.java | 3238 ++++++ src/javax/media/j3d/SoundSchedulerAtom.java | 707 ++ src/javax/media/j3d/SoundStructure.java | 741 ++ src/javax/media/j3d/Soundscape.java | 361 + src/javax/media/j3d/SoundscapeRetained.java | 458 + src/javax/media/j3d/SourceCodeShader.java | 138 + src/javax/media/j3d/SourceCodeShaderRetained.java | 102 + src/javax/media/j3d/SpotLight.java | 375 + src/javax/media/j3d/SpotLightRetained.java | 322 + src/javax/media/j3d/StructureUpdateThread.java | 100 + src/javax/media/j3d/Switch.java | 277 + src/javax/media/j3d/SwitchRetained.java | 957 ++ src/javax/media/j3d/SwitchState.java | 126 + src/javax/media/j3d/SwitchValueInterpolator.java | 293 + src/javax/media/j3d/Targets.java | 196 + src/javax/media/j3d/TargetsInterface.java | 49 + src/javax/media/j3d/TexCoordGeneration.java | 686 ++ .../media/j3d/TexCoordGenerationRetained.java | 416 + src/javax/media/j3d/Text3D.java | 661 ++ src/javax/media/j3d/Text3DRenderMethod.java | 114 + src/javax/media/j3d/Text3DRetained.java | 999 ++ src/javax/media/j3d/Texture.java | 1877 ++++ src/javax/media/j3d/Texture2D.java | 572 + src/javax/media/j3d/Texture2DRetained.java | 202 + src/javax/media/j3d/Texture3D.java | 266 + src/javax/media/j3d/Texture3DRetained.java | 240 + src/javax/media/j3d/TextureAttributes.java | 1452 +++ src/javax/media/j3d/TextureAttributesRetained.java | 996 ++ src/javax/media/j3d/TextureBin.java | 1543 +++ src/javax/media/j3d/TextureCubeMap.java | 403 + src/javax/media/j3d/TextureCubeMapRetained.java | 355 + src/javax/media/j3d/TextureRetained.java | 2515 +++++ src/javax/media/j3d/TextureUnitState.java | 389 + src/javax/media/j3d/TextureUnitStateRetained.java | 614 + src/javax/media/j3d/TimerThread.java | 167 + src/javax/media/j3d/Transform3D.java | 5870 ++++++++++ src/javax/media/j3d/TransformGroup.java | 206 + src/javax/media/j3d/TransformGroupData.java | 37 + src/javax/media/j3d/TransformGroupRetained.java | 1270 +++ src/javax/media/j3d/TransformInterpolator.java | 250 + src/javax/media/j3d/TransformStructure.java | 759 ++ src/javax/media/j3d/TransparencyAttributes.java | 643 ++ .../media/j3d/TransparencyAttributesRetained.java | 353 + src/javax/media/j3d/TransparencyInterpolator.java | 288 + src/javax/media/j3d/TransparencySortGeom.java | 49 + src/javax/media/j3d/TransparencySortMap.java | 48 + src/javax/media/j3d/TransparentRenderingInfo.java | 163 + src/javax/media/j3d/TriangleArray.java | 191 + src/javax/media/j3d/TriangleArrayRetained.java | 491 + src/javax/media/j3d/TriangleFanArray.java | 219 + src/javax/media/j3d/TriangleFanArrayRetained.java | 597 + src/javax/media/j3d/TriangleStripArray.java | 217 + .../media/j3d/TriangleStripArrayRetained.java | 623 ++ src/javax/media/j3d/UnorderList.java | 590 + src/javax/media/j3d/UpdateTargets.java | 113 + src/javax/media/j3d/Utils.java | 746 ++ src/javax/media/j3d/VertexArrayRenderMethod.java | 98 + src/javax/media/j3d/View.java | 3336 ++++++ src/javax/media/j3d/ViewCache.java | 356 + src/javax/media/j3d/ViewPlatform.java | 289 + src/javax/media/j3d/ViewPlatformRetained.java | 418 + src/javax/media/j3d/ViewSpecificGroup.java | 360 + src/javax/media/j3d/ViewSpecificGroupRetained.java | 759 ++ src/javax/media/j3d/VirtualUniverse.java | 1220 ++ src/javax/media/j3d/WakeupAnd.java | 133 + src/javax/media/j3d/WakeupAndOfOrs.java | 134 + src/javax/media/j3d/WakeupCondition.java | 146 + src/javax/media/j3d/WakeupCriteriaEnumerator.java | 149 + src/javax/media/j3d/WakeupCriterion.java | 125 + src/javax/media/j3d/WakeupIndexedList.java | 600 + src/javax/media/j3d/WakeupOnAWTEvent.java | 165 + src/javax/media/j3d/WakeupOnActivation.java | 82 + src/javax/media/j3d/WakeupOnBehaviorPost.java | 130 + src/javax/media/j3d/WakeupOnCollisionEntry.java | 595 + src/javax/media/j3d/WakeupOnCollisionExit.java | 394 + src/javax/media/j3d/WakeupOnCollisionMovement.java | 402 + src/javax/media/j3d/WakeupOnDeactivation.java | 96 + src/javax/media/j3d/WakeupOnElapsedFrames.java | 181 + src/javax/media/j3d/WakeupOnElapsedTime.java | 112 + src/javax/media/j3d/WakeupOnElapsedTimeHeap.java | 226 + src/javax/media/j3d/WakeupOnSensorEntry.java | 146 + src/javax/media/j3d/WakeupOnSensorExit.java | 145 + src/javax/media/j3d/WakeupOnTransformChange.java | 96 + src/javax/media/j3d/WakeupOnViewPlatformEntry.java | 150 + src/javax/media/j3d/WakeupOnViewPlatformExit.java | 152 + src/javax/media/j3d/WakeupOr.java | 118 + src/javax/media/j3d/WakeupOrOfAnds.java | 118 + src/javax/media/j3d/doc-files/Behaviors.html | 596 + src/javax/media/j3d/doc-files/Behaviors1.gif | Bin 0 -> 9067 bytes src/javax/media/j3d/doc-files/Behaviors2.gif | Bin 0 -> 2223 bytes src/javax/media/j3d/doc-files/Behaviors3.gif | Bin 0 -> 2189 bytes src/javax/media/j3d/doc-files/Behaviors4.gif | Bin 0 -> 2452 bytes src/javax/media/j3d/doc-files/Behaviors5.gif | Bin 0 -> 4743 bytes src/javax/media/j3d/doc-files/Behaviors6.gif | Bin 0 -> 4535 bytes src/javax/media/j3d/doc-files/Behaviors7.gif | Bin 0 -> 4463 bytes src/javax/media/j3d/doc-files/Behaviors8.gif | Bin 0 -> 22297 bytes src/javax/media/j3d/doc-files/Concepts.html | 291 + src/javax/media/j3d/doc-files/Concepts1.gif | Bin 0 -> 19058 bytes src/javax/media/j3d/doc-files/Concepts2.gif | Bin 0 -> 14546 bytes src/javax/media/j3d/doc-files/DAG.gif | Bin 0 -> 19099 bytes src/javax/media/j3d/doc-files/HelloUniverse.html | 21 + src/javax/media/j3d/doc-files/Immediate.html | 114 + src/javax/media/j3d/doc-files/Immediate1.gif | Bin 0 -> 17085 bytes src/javax/media/j3d/doc-files/Rendering.html | 148 + .../media/j3d/doc-files/SceneGraphOverview.html | 226 + .../media/j3d/doc-files/SceneGraphSharing.html | 250 + .../media/j3d/doc-files/SceneGraphSharing1.gif | Bin 0 -> 22601 bytes .../media/j3d/doc-files/SceneGraphSharing2.gif | Bin 0 -> 17159 bytes .../media/j3d/doc-files/SceneGraphSharing3.gif | Bin 0 -> 13186 bytes .../media/j3d/doc-files/SceneGraphSharing4.gif | Bin 0 -> 12419 bytes .../media/j3d/doc-files/SceneGraphSharing5.gif | Bin 0 -> 10695 bytes src/javax/media/j3d/doc-files/ViewBranch.gif | Bin 0 -> 19210 bytes src/javax/media/j3d/doc-files/ViewModel.html | 1064 ++ src/javax/media/j3d/doc-files/ViewModel1.gif | Bin 0 -> 19585 bytes src/javax/media/j3d/doc-files/ViewModel10.gif | Bin 0 -> 29787 bytes src/javax/media/j3d/doc-files/ViewModel11.gif | Bin 0 -> 15569 bytes src/javax/media/j3d/doc-files/ViewModel12.gif | Bin 0 -> 15520 bytes src/javax/media/j3d/doc-files/ViewModel13.gif | Bin 0 -> 14982 bytes src/javax/media/j3d/doc-files/ViewModel14.gif | Bin 0 -> 11968 bytes src/javax/media/j3d/doc-files/ViewModel2.gif | Bin 0 -> 17085 bytes src/javax/media/j3d/doc-files/ViewModel3.gif | Bin 0 -> 22430 bytes src/javax/media/j3d/doc-files/ViewModel4.gif | Bin 0 -> 16502 bytes src/javax/media/j3d/doc-files/ViewModel5.gif | Bin 0 -> 9524 bytes src/javax/media/j3d/doc-files/ViewModel6.gif | Bin 0 -> 10590 bytes src/javax/media/j3d/doc-files/ViewModel7.gif | Bin 0 -> 10958 bytes src/javax/media/j3d/doc-files/ViewModel8.gif | Bin 0 -> 16583 bytes src/javax/media/j3d/doc-files/ViewModel9.gif | Bin 0 -> 14194 bytes src/javax/media/j3d/doc-files/VirtualUniverse.gif | Bin 0 -> 19123 bytes src/javax/media/j3d/doc-files/VirtualUniverse.html | 265 + src/javax/media/j3d/doc-files/intro.gif | Bin 0 -> 19452 bytes src/javax/media/j3d/doc-files/intro.html | 337 + src/javax/media/j3d/package.html | 40 + 448 files changed, 231242 insertions(+) create mode 100644 src/javax/media/j3d/Alpha.java create mode 100644 src/javax/media/j3d/AlternateAppearance.java create mode 100644 src/javax/media/j3d/AlternateAppearanceRetained.java create mode 100644 src/javax/media/j3d/AmbientLight.java create mode 100644 src/javax/media/j3d/AmbientLightRetained.java create mode 100644 src/javax/media/j3d/Appearance.java create mode 100644 src/javax/media/j3d/AppearanceRetained.java create mode 100644 src/javax/media/j3d/AssertionFailureException.java create mode 100644 src/javax/media/j3d/AttributeBin.java create mode 100644 src/javax/media/j3d/AudioDevice.java create mode 100644 src/javax/media/j3d/AudioDevice3D.java create mode 100644 src/javax/media/j3d/AudioDevice3DL2.java create mode 100644 src/javax/media/j3d/AudioDeviceEnumerator.java create mode 100644 src/javax/media/j3d/AuralAttributes.java create mode 100644 src/javax/media/j3d/AuralAttributesRetained.java create mode 100644 src/javax/media/j3d/AutoOffScreenCanvas3D.java create mode 100644 src/javax/media/j3d/BHInsertStructure.java create mode 100644 src/javax/media/j3d/BHInternalNode.java create mode 100644 src/javax/media/j3d/BHLeafInterface.java create mode 100644 src/javax/media/j3d/BHLeafNode.java create mode 100644 src/javax/media/j3d/BHNode.java create mode 100644 src/javax/media/j3d/BHTree.java create mode 100644 src/javax/media/j3d/Background.java create mode 100644 src/javax/media/j3d/BackgroundRetained.java create mode 100644 src/javax/media/j3d/BackgroundSound.java create mode 100644 src/javax/media/j3d/BackgroundSoundRetained.java create mode 100644 src/javax/media/j3d/BadTransformException.java create mode 100644 src/javax/media/j3d/Behavior.java create mode 100644 src/javax/media/j3d/BehaviorRetained.java create mode 100644 src/javax/media/j3d/BehaviorScheduler.java create mode 100644 src/javax/media/j3d/BehaviorStructure.java create mode 100644 src/javax/media/j3d/Billboard.java create mode 100644 src/javax/media/j3d/BoundingBox.java create mode 100644 src/javax/media/j3d/BoundingLeaf.java create mode 100644 src/javax/media/j3d/BoundingLeafRetained.java create mode 100644 src/javax/media/j3d/BoundingPolytope.java create mode 100644 src/javax/media/j3d/BoundingSphere.java create mode 100644 src/javax/media/j3d/Bounds.java create mode 100644 src/javax/media/j3d/BranchGroup.java create mode 100644 src/javax/media/j3d/BranchGroupRetained.java create mode 100644 src/javax/media/j3d/CachedFrustum.java create mode 100644 src/javax/media/j3d/CachedTargets.java create mode 100644 src/javax/media/j3d/Canvas3D.java create mode 100644 src/javax/media/j3d/CanvasViewCache.java create mode 100644 src/javax/media/j3d/CanvasViewEventCatcher.java create mode 100644 src/javax/media/j3d/CapabilityBits.java create mode 100644 src/javax/media/j3d/CapabilityNotSetException.java create mode 100644 src/javax/media/j3d/Clip.java create mode 100644 src/javax/media/j3d/ClipRetained.java create mode 100644 src/javax/media/j3d/ColorInterpolator.java create mode 100644 src/javax/media/j3d/ColoringAttributes.java create mode 100644 src/javax/media/j3d/ColoringAttributesRetained.java create mode 100644 src/javax/media/j3d/CompileState.java create mode 100644 src/javax/media/j3d/CompressedGeometry.java create mode 100644 src/javax/media/j3d/CompressedGeometryHeader.java create mode 100644 src/javax/media/j3d/CompressedGeometryRenderMethod.java create mode 100644 src/javax/media/j3d/CompressedGeometryRetained.java create mode 100644 src/javax/media/j3d/ConeSound.java create mode 100644 src/javax/media/j3d/ConeSoundRetained.java create mode 100644 src/javax/media/j3d/Context.java create mode 100644 src/javax/media/j3d/DanglingReferenceException.java create mode 100644 src/javax/media/j3d/DecalGroup.java create mode 100644 src/javax/media/j3d/DecalGroupRetained.java create mode 100644 src/javax/media/j3d/DefaultRenderMethod.java create mode 100644 src/javax/media/j3d/DepthComponent.java create mode 100644 src/javax/media/j3d/DepthComponentFloat.java create mode 100644 src/javax/media/j3d/DepthComponentFloatRetained.java create mode 100644 src/javax/media/j3d/DepthComponentInt.java create mode 100644 src/javax/media/j3d/DepthComponentIntRetained.java create mode 100644 src/javax/media/j3d/DepthComponentNative.java create mode 100644 src/javax/media/j3d/DepthComponentNativeRetained.java create mode 100644 src/javax/media/j3d/DepthComponentRetained.java create mode 100644 src/javax/media/j3d/DirectionalLight.java create mode 100644 src/javax/media/j3d/DirectionalLightRetained.java create mode 100644 src/javax/media/j3d/DisplayListRenderMethod.java create mode 100644 src/javax/media/j3d/DistanceLOD.java create mode 100644 src/javax/media/j3d/Drawable.java create mode 100644 src/javax/media/j3d/DrawingSurfaceObject.java create mode 100644 src/javax/media/j3d/EnvironmentSet.java create mode 100644 src/javax/media/j3d/EventCatcher.java create mode 100644 src/javax/media/j3d/ExceptionStrings.properties create mode 100644 src/javax/media/j3d/ExponentialFog.java create mode 100644 src/javax/media/j3d/ExponentialFogRetained.java create mode 100644 src/javax/media/j3d/Fog.java create mode 100644 src/javax/media/j3d/FogRetained.java create mode 100644 src/javax/media/j3d/Font3D.java create mode 100644 src/javax/media/j3d/FontExtrusion.java create mode 100644 src/javax/media/j3d/FreeListManager.java create mode 100644 src/javax/media/j3d/GLSLShaderProgram.java create mode 100644 src/javax/media/j3d/GLSLShaderProgramRetained.java create mode 100644 src/javax/media/j3d/GeneralizedStrip.java create mode 100644 src/javax/media/j3d/GeneralizedStripFlags.java create mode 100644 src/javax/media/j3d/GeneralizedVertexList.java create mode 100644 src/javax/media/j3d/Geometry.java create mode 100644 src/javax/media/j3d/GeometryArray.java create mode 100644 src/javax/media/j3d/GeometryArrayRetained.java create mode 100644 src/javax/media/j3d/GeometryAtom.java create mode 100644 src/javax/media/j3d/GeometryDecompressor.java create mode 100644 src/javax/media/j3d/GeometryDecompressorRetained.java create mode 100644 src/javax/media/j3d/GeometryDecompressorShape3D.java create mode 100644 src/javax/media/j3d/GeometryLock.java create mode 100644 src/javax/media/j3d/GeometryRetained.java create mode 100644 src/javax/media/j3d/GeometryStripArray.java create mode 100644 src/javax/media/j3d/GeometryStripArrayRetained.java create mode 100644 src/javax/media/j3d/GeometryStructure.java create mode 100644 src/javax/media/j3d/GeometryUpdater.java create mode 100644 src/javax/media/j3d/GraphStructureChangeListener.java create mode 100644 src/javax/media/j3d/GraphicsConfigInfo.java create mode 100644 src/javax/media/j3d/GraphicsConfigTemplate3D.java create mode 100644 src/javax/media/j3d/GraphicsContext3D.java create mode 100644 src/javax/media/j3d/Group.java create mode 100644 src/javax/media/j3d/GroupRetained.java create mode 100644 src/javax/media/j3d/HashKey.java create mode 100644 src/javax/media/j3d/HiResCoord.java create mode 100644 src/javax/media/j3d/IllegalRenderingStateException.java create mode 100644 src/javax/media/j3d/IllegalSceneGraphException.java create mode 100644 src/javax/media/j3d/IllegalSharingException.java create mode 100644 src/javax/media/j3d/ImageComponent.java create mode 100644 src/javax/media/j3d/ImageComponent2D.java create mode 100644 src/javax/media/j3d/ImageComponent2DRetained.java create mode 100644 src/javax/media/j3d/ImageComponent3D.java create mode 100644 src/javax/media/j3d/ImageComponent3DRetained.java create mode 100644 src/javax/media/j3d/ImageComponentRetained.java create mode 100644 src/javax/media/j3d/ImageComponentUpdateInfo.java create mode 100644 src/javax/media/j3d/IndexedGeometryArray.java create mode 100644 src/javax/media/j3d/IndexedGeometryArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedGeometryStripArray.java create mode 100644 src/javax/media/j3d/IndexedGeometryStripArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedLineArray.java create mode 100644 src/javax/media/j3d/IndexedLineArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedLineStripArray.java create mode 100644 src/javax/media/j3d/IndexedLineStripArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedObject.java create mode 100644 src/javax/media/j3d/IndexedPointArray.java create mode 100644 src/javax/media/j3d/IndexedPointArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedQuadArray.java create mode 100644 src/javax/media/j3d/IndexedQuadArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedTriangleArray.java create mode 100644 src/javax/media/j3d/IndexedTriangleArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedTriangleFanArray.java create mode 100644 src/javax/media/j3d/IndexedTriangleFanArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedTriangleStripArray.java create mode 100644 src/javax/media/j3d/IndexedTriangleStripArrayRetained.java create mode 100644 src/javax/media/j3d/IndexedUnorderSet.java create mode 100644 src/javax/media/j3d/InputDevice.java create mode 100644 src/javax/media/j3d/InputDeviceBlockingThread.java create mode 100644 src/javax/media/j3d/InputDeviceScheduler.java create mode 100644 src/javax/media/j3d/IntegerFreeList.java create mode 100644 src/javax/media/j3d/Interpolator.java create mode 100644 src/javax/media/j3d/J3DBuffer.java create mode 100644 src/javax/media/j3d/J3DGraphics2D.java create mode 100644 src/javax/media/j3d/J3DGraphics2DImpl.java create mode 100644 src/javax/media/j3d/J3dClock.java create mode 100644 src/javax/media/j3d/J3dDebug.java create mode 100644 src/javax/media/j3d/J3dHash.java create mode 100644 src/javax/media/j3d/J3dI18N.java create mode 100644 src/javax/media/j3d/J3dMessage.java create mode 100644 src/javax/media/j3d/J3dNotification.java create mode 100644 src/javax/media/j3d/J3dQueryProps.java create mode 100644 src/javax/media/j3d/J3dStructure.java create mode 100644 src/javax/media/j3d/J3dThread.java create mode 100644 src/javax/media/j3d/J3dThreadData.java create mode 100644 src/javax/media/j3d/JoglContext.java create mode 100644 src/javax/media/j3d/JoglDrawable.java create mode 100644 src/javax/media/j3d/JoglDrawingSurfaceObject.java create mode 100644 src/javax/media/j3d/JoglGraphicsConfiguration.java create mode 100644 src/javax/media/j3d/JoglPipeline.java create mode 100644 src/javax/media/j3d/JoglShaderObject.java create mode 100644 src/javax/media/j3d/LOD.java create mode 100644 src/javax/media/j3d/Leaf.java create mode 100644 src/javax/media/j3d/LeafRetained.java create mode 100644 src/javax/media/j3d/Light.java create mode 100644 src/javax/media/j3d/LightBin.java create mode 100644 src/javax/media/j3d/LightRetained.java create mode 100644 src/javax/media/j3d/LightSet.java create mode 100644 src/javax/media/j3d/LineArray.java create mode 100644 src/javax/media/j3d/LineArrayRetained.java create mode 100644 src/javax/media/j3d/LineAttributes.java create mode 100644 src/javax/media/j3d/LineAttributesRetained.java create mode 100644 src/javax/media/j3d/LineStripArray.java create mode 100644 src/javax/media/j3d/LineStripArrayRetained.java create mode 100644 src/javax/media/j3d/LinearFog.java create mode 100644 src/javax/media/j3d/LinearFogRetained.java create mode 100644 src/javax/media/j3d/Link.java create mode 100644 src/javax/media/j3d/LinkRetained.java create mode 100644 src/javax/media/j3d/Locale.java create mode 100644 src/javax/media/j3d/MRSWLock.java create mode 100644 src/javax/media/j3d/MasterControl.java create mode 100644 src/javax/media/j3d/MasterControlThread.java create mode 100644 src/javax/media/j3d/Material.java create mode 100644 src/javax/media/j3d/MaterialRetained.java create mode 100644 src/javax/media/j3d/MediaContainer.java create mode 100644 src/javax/media/j3d/MediaContainerRetained.java create mode 100644 src/javax/media/j3d/MemoryFreeList.java create mode 100644 src/javax/media/j3d/ModelClip.java create mode 100644 src/javax/media/j3d/ModelClipRetained.java create mode 100644 src/javax/media/j3d/Morph.java create mode 100644 src/javax/media/j3d/MorphRetained.java create mode 100644 src/javax/media/j3d/MultipleParentException.java create mode 100644 src/javax/media/j3d/NioImageBuffer.java create mode 100644 src/javax/media/j3d/NnuId.java create mode 100644 src/javax/media/j3d/NnuIdManager.java create mode 100644 src/javax/media/j3d/Node.java create mode 100644 src/javax/media/j3d/NodeComponent.java create mode 100644 src/javax/media/j3d/NodeComponentRetained.java create mode 100644 src/javax/media/j3d/NodeComponentUpdate.java create mode 100644 src/javax/media/j3d/NodeData.java create mode 100644 src/javax/media/j3d/NodeReferenceTable.java create mode 100644 src/javax/media/j3d/NodeRetained.java create mode 100644 src/javax/media/j3d/NoopDrawingSurfaceObject.java create mode 100644 src/javax/media/j3d/NoopPipeline.java create mode 100644 src/javax/media/j3d/NotificationThread.java create mode 100644 src/javax/media/j3d/ObjectUpdate.java create mode 100644 src/javax/media/j3d/OrderedBin.java create mode 100644 src/javax/media/j3d/OrderedChildInfo.java create mode 100644 src/javax/media/j3d/OrderedCollection.java create mode 100644 src/javax/media/j3d/OrderedGroup.java create mode 100644 src/javax/media/j3d/OrderedGroupRetained.java create mode 100644 src/javax/media/j3d/OrderedPath.java create mode 100644 src/javax/media/j3d/OrderedPathElement.java create mode 100644 src/javax/media/j3d/OrientedShape3D.java create mode 100644 src/javax/media/j3d/OrientedShape3DRenderMethod.java create mode 100644 src/javax/media/j3d/OrientedShape3DRetained.java create mode 100644 src/javax/media/j3d/PathInterpolator.java create mode 100644 src/javax/media/j3d/PhysicalBody.java create mode 100644 src/javax/media/j3d/PhysicalEnvironment.java create mode 100644 src/javax/media/j3d/PickBounds.java create mode 100644 src/javax/media/j3d/PickCone.java create mode 100644 src/javax/media/j3d/PickConeRay.java create mode 100644 src/javax/media/j3d/PickConeSegment.java create mode 100644 src/javax/media/j3d/PickCylinder.java create mode 100644 src/javax/media/j3d/PickCylinderRay.java create mode 100644 src/javax/media/j3d/PickCylinderSegment.java create mode 100644 src/javax/media/j3d/PickInfo.java create mode 100644 src/javax/media/j3d/PickPoint.java create mode 100644 src/javax/media/j3d/PickRay.java create mode 100644 src/javax/media/j3d/PickSegment.java create mode 100644 src/javax/media/j3d/PickShape.java create mode 100644 src/javax/media/j3d/Pipeline.java create mode 100644 src/javax/media/j3d/PointArray.java create mode 100644 src/javax/media/j3d/PointArrayRetained.java create mode 100644 src/javax/media/j3d/PointAttributes.java create mode 100644 src/javax/media/j3d/PointAttributesRetained.java create mode 100644 src/javax/media/j3d/PointLight.java create mode 100644 src/javax/media/j3d/PointLightRetained.java create mode 100644 src/javax/media/j3d/PointSound.java create mode 100644 src/javax/media/j3d/PointSoundRetained.java create mode 100644 src/javax/media/j3d/PolygonAttributes.java create mode 100644 src/javax/media/j3d/PolygonAttributesRetained.java create mode 100644 src/javax/media/j3d/PositionInterpolator.java create mode 100644 src/javax/media/j3d/PositionPathInterpolator.java create mode 100644 src/javax/media/j3d/QuadArray.java create mode 100644 src/javax/media/j3d/QuadArrayRetained.java create mode 100644 src/javax/media/j3d/Raster.java create mode 100644 src/javax/media/j3d/RasterRetained.java create mode 100644 src/javax/media/j3d/RenderAtom.java create mode 100644 src/javax/media/j3d/RenderAtomListInfo.java create mode 100644 src/javax/media/j3d/RenderBin.java create mode 100644 src/javax/media/j3d/RenderMethod.java create mode 100644 src/javax/media/j3d/RenderMolecule.java create mode 100644 src/javax/media/j3d/Renderer.java create mode 100644 src/javax/media/j3d/RendererStructure.java create mode 100644 src/javax/media/j3d/RenderingAttributes.java create mode 100644 src/javax/media/j3d/RenderingAttributesRetained.java create mode 100644 src/javax/media/j3d/RenderingAttributesStructure.java create mode 100644 src/javax/media/j3d/RenderingEnvironmentStructure.java create mode 100644 src/javax/media/j3d/RenderingError.java create mode 100644 src/javax/media/j3d/RenderingErrorListener.java create mode 100644 src/javax/media/j3d/RestrictedAccessException.java create mode 100644 src/javax/media/j3d/RotPosPathInterpolator.java create mode 100644 src/javax/media/j3d/RotPosScalePathInterpolator.java create mode 100644 src/javax/media/j3d/RotationInterpolator.java create mode 100644 src/javax/media/j3d/RotationPathInterpolator.java create mode 100644 src/javax/media/j3d/ScaleInterpolator.java create mode 100644 src/javax/media/j3d/SceneGraphCycleException.java create mode 100644 src/javax/media/j3d/SceneGraphObject.java create mode 100644 src/javax/media/j3d/SceneGraphObjectRetained.java create mode 100644 src/javax/media/j3d/SceneGraphPath.java create mode 100644 src/javax/media/j3d/Screen3D.java create mode 100644 src/javax/media/j3d/ScreenViewCache.java create mode 100644 src/javax/media/j3d/Sensor.java create mode 100644 src/javax/media/j3d/SensorRead.java create mode 100644 src/javax/media/j3d/SetLiveState.java create mode 100644 src/javax/media/j3d/Shader.java create mode 100644 src/javax/media/j3d/ShaderAppearance.java create mode 100644 src/javax/media/j3d/ShaderAppearanceRetained.java create mode 100644 src/javax/media/j3d/ShaderAttrLoc.java create mode 100644 src/javax/media/j3d/ShaderAttribute.java create mode 100644 src/javax/media/j3d/ShaderAttributeArray.java create mode 100644 src/javax/media/j3d/ShaderAttributeArrayRetained.java create mode 100644 src/javax/media/j3d/ShaderAttributeBinding.java create mode 100644 src/javax/media/j3d/ShaderAttributeBindingRetained.java create mode 100644 src/javax/media/j3d/ShaderAttributeObject.java create mode 100644 src/javax/media/j3d/ShaderAttributeObjectRetained.java create mode 100644 src/javax/media/j3d/ShaderAttributeRetained.java create mode 100644 src/javax/media/j3d/ShaderAttributeSet.java create mode 100644 src/javax/media/j3d/ShaderAttributeSetRetained.java create mode 100644 src/javax/media/j3d/ShaderAttributeValue.java create mode 100644 src/javax/media/j3d/ShaderAttributeValueRetained.java create mode 100644 src/javax/media/j3d/ShaderBin.java create mode 100644 src/javax/media/j3d/ShaderConstants.java create mode 100644 src/javax/media/j3d/ShaderError.java create mode 100644 src/javax/media/j3d/ShaderErrorListener.java create mode 100644 src/javax/media/j3d/ShaderId.java create mode 100644 src/javax/media/j3d/ShaderProgram.java create mode 100644 src/javax/media/j3d/ShaderProgramId.java create mode 100644 src/javax/media/j3d/ShaderProgramRetained.java create mode 100644 src/javax/media/j3d/ShaderRetained.java create mode 100644 src/javax/media/j3d/Shape3D.java create mode 100644 src/javax/media/j3d/Shape3DCompileRetained.java create mode 100644 src/javax/media/j3d/Shape3DRetained.java create mode 100644 src/javax/media/j3d/SharedGroup.java create mode 100644 src/javax/media/j3d/SharedGroupRetained.java create mode 100644 src/javax/media/j3d/Sound.java create mode 100644 src/javax/media/j3d/SoundException.java create mode 100644 src/javax/media/j3d/SoundRetained.java create mode 100644 src/javax/media/j3d/SoundScheduler.java create mode 100644 src/javax/media/j3d/SoundSchedulerAtom.java create mode 100644 src/javax/media/j3d/SoundStructure.java create mode 100644 src/javax/media/j3d/Soundscape.java create mode 100644 src/javax/media/j3d/SoundscapeRetained.java create mode 100644 src/javax/media/j3d/SourceCodeShader.java create mode 100644 src/javax/media/j3d/SourceCodeShaderRetained.java create mode 100644 src/javax/media/j3d/SpotLight.java create mode 100644 src/javax/media/j3d/SpotLightRetained.java create mode 100644 src/javax/media/j3d/StructureUpdateThread.java create mode 100644 src/javax/media/j3d/Switch.java create mode 100644 src/javax/media/j3d/SwitchRetained.java create mode 100644 src/javax/media/j3d/SwitchState.java create mode 100644 src/javax/media/j3d/SwitchValueInterpolator.java create mode 100644 src/javax/media/j3d/Targets.java create mode 100644 src/javax/media/j3d/TargetsInterface.java create mode 100644 src/javax/media/j3d/TexCoordGeneration.java create mode 100644 src/javax/media/j3d/TexCoordGenerationRetained.java create mode 100644 src/javax/media/j3d/Text3D.java create mode 100644 src/javax/media/j3d/Text3DRenderMethod.java create mode 100644 src/javax/media/j3d/Text3DRetained.java create mode 100644 src/javax/media/j3d/Texture.java create mode 100644 src/javax/media/j3d/Texture2D.java create mode 100644 src/javax/media/j3d/Texture2DRetained.java create mode 100644 src/javax/media/j3d/Texture3D.java create mode 100644 src/javax/media/j3d/Texture3DRetained.java create mode 100644 src/javax/media/j3d/TextureAttributes.java create mode 100644 src/javax/media/j3d/TextureAttributesRetained.java create mode 100644 src/javax/media/j3d/TextureBin.java create mode 100644 src/javax/media/j3d/TextureCubeMap.java create mode 100644 src/javax/media/j3d/TextureCubeMapRetained.java create mode 100644 src/javax/media/j3d/TextureRetained.java create mode 100644 src/javax/media/j3d/TextureUnitState.java create mode 100644 src/javax/media/j3d/TextureUnitStateRetained.java create mode 100644 src/javax/media/j3d/TimerThread.java create mode 100644 src/javax/media/j3d/Transform3D.java create mode 100644 src/javax/media/j3d/TransformGroup.java create mode 100644 src/javax/media/j3d/TransformGroupData.java create mode 100644 src/javax/media/j3d/TransformGroupRetained.java create mode 100644 src/javax/media/j3d/TransformInterpolator.java create mode 100644 src/javax/media/j3d/TransformStructure.java create mode 100644 src/javax/media/j3d/TransparencyAttributes.java create mode 100644 src/javax/media/j3d/TransparencyAttributesRetained.java create mode 100644 src/javax/media/j3d/TransparencyInterpolator.java create mode 100644 src/javax/media/j3d/TransparencySortGeom.java create mode 100644 src/javax/media/j3d/TransparencySortMap.java create mode 100644 src/javax/media/j3d/TransparentRenderingInfo.java create mode 100644 src/javax/media/j3d/TriangleArray.java create mode 100644 src/javax/media/j3d/TriangleArrayRetained.java create mode 100644 src/javax/media/j3d/TriangleFanArray.java create mode 100644 src/javax/media/j3d/TriangleFanArrayRetained.java create mode 100644 src/javax/media/j3d/TriangleStripArray.java create mode 100644 src/javax/media/j3d/TriangleStripArrayRetained.java create mode 100644 src/javax/media/j3d/UnorderList.java create mode 100644 src/javax/media/j3d/UpdateTargets.java create mode 100644 src/javax/media/j3d/Utils.java create mode 100644 src/javax/media/j3d/VertexArrayRenderMethod.java create mode 100644 src/javax/media/j3d/View.java create mode 100644 src/javax/media/j3d/ViewCache.java create mode 100644 src/javax/media/j3d/ViewPlatform.java create mode 100644 src/javax/media/j3d/ViewPlatformRetained.java create mode 100644 src/javax/media/j3d/ViewSpecificGroup.java create mode 100644 src/javax/media/j3d/ViewSpecificGroupRetained.java create mode 100644 src/javax/media/j3d/VirtualUniverse.java create mode 100644 src/javax/media/j3d/WakeupAnd.java create mode 100644 src/javax/media/j3d/WakeupAndOfOrs.java create mode 100644 src/javax/media/j3d/WakeupCondition.java create mode 100644 src/javax/media/j3d/WakeupCriteriaEnumerator.java create mode 100644 src/javax/media/j3d/WakeupCriterion.java create mode 100644 src/javax/media/j3d/WakeupIndexedList.java create mode 100644 src/javax/media/j3d/WakeupOnAWTEvent.java create mode 100644 src/javax/media/j3d/WakeupOnActivation.java create mode 100644 src/javax/media/j3d/WakeupOnBehaviorPost.java create mode 100644 src/javax/media/j3d/WakeupOnCollisionEntry.java create mode 100644 src/javax/media/j3d/WakeupOnCollisionExit.java create mode 100644 src/javax/media/j3d/WakeupOnCollisionMovement.java create mode 100644 src/javax/media/j3d/WakeupOnDeactivation.java create mode 100644 src/javax/media/j3d/WakeupOnElapsedFrames.java create mode 100644 src/javax/media/j3d/WakeupOnElapsedTime.java create mode 100644 src/javax/media/j3d/WakeupOnElapsedTimeHeap.java create mode 100644 src/javax/media/j3d/WakeupOnSensorEntry.java create mode 100644 src/javax/media/j3d/WakeupOnSensorExit.java create mode 100644 src/javax/media/j3d/WakeupOnTransformChange.java create mode 100644 src/javax/media/j3d/WakeupOnViewPlatformEntry.java create mode 100644 src/javax/media/j3d/WakeupOnViewPlatformExit.java create mode 100644 src/javax/media/j3d/WakeupOr.java create mode 100644 src/javax/media/j3d/WakeupOrOfAnds.java create mode 100644 src/javax/media/j3d/doc-files/Behaviors.html create mode 100644 src/javax/media/j3d/doc-files/Behaviors1.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors2.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors3.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors4.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors5.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors6.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors7.gif create mode 100644 src/javax/media/j3d/doc-files/Behaviors8.gif create mode 100644 src/javax/media/j3d/doc-files/Concepts.html create mode 100644 src/javax/media/j3d/doc-files/Concepts1.gif create mode 100644 src/javax/media/j3d/doc-files/Concepts2.gif create mode 100644 src/javax/media/j3d/doc-files/DAG.gif create mode 100644 src/javax/media/j3d/doc-files/HelloUniverse.html create mode 100644 src/javax/media/j3d/doc-files/Immediate.html create mode 100644 src/javax/media/j3d/doc-files/Immediate1.gif create mode 100644 src/javax/media/j3d/doc-files/Rendering.html create mode 100644 src/javax/media/j3d/doc-files/SceneGraphOverview.html create mode 100644 src/javax/media/j3d/doc-files/SceneGraphSharing.html create mode 100644 src/javax/media/j3d/doc-files/SceneGraphSharing1.gif create mode 100644 src/javax/media/j3d/doc-files/SceneGraphSharing2.gif create mode 100644 src/javax/media/j3d/doc-files/SceneGraphSharing3.gif create mode 100644 src/javax/media/j3d/doc-files/SceneGraphSharing4.gif create mode 100644 src/javax/media/j3d/doc-files/SceneGraphSharing5.gif create mode 100644 src/javax/media/j3d/doc-files/ViewBranch.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel.html create mode 100644 src/javax/media/j3d/doc-files/ViewModel1.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel10.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel11.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel12.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel13.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel14.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel2.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel3.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel4.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel5.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel6.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel7.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel8.gif create mode 100644 src/javax/media/j3d/doc-files/ViewModel9.gif create mode 100644 src/javax/media/j3d/doc-files/VirtualUniverse.gif create mode 100644 src/javax/media/j3d/doc-files/VirtualUniverse.html create mode 100644 src/javax/media/j3d/doc-files/intro.gif create mode 100644 src/javax/media/j3d/doc-files/intro.html create mode 100644 src/javax/media/j3d/package.html (limited to 'src/javax/media/j3d') diff --git a/src/javax/media/j3d/Alpha.java b/src/javax/media/j3d/Alpha.java new file mode 100644 index 0000000..8f3cb2f --- /dev/null +++ b/src/javax/media/j3d/Alpha.java @@ -0,0 +1,1022 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + * + *

+ *

Increasing Alpha parameters:

Decreasing Alpha parameters:

+ * + * @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; + + // Start time in milliseconds + private long startTime = MasterControl.systemStartTime; + + /** + * Constructs an Alpha object with default parameters. The default + * values are as follows: + * + */ + 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(J3dClock.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: + * + * + * + * 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(J3dClock.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: + * + * + * + * 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 : J3dClock.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.err.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 : J3dClock.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; + } + + static { + VirtualUniverse.loadLibraries(); + } + +} diff --git a/src/javax/media/j3d/AlternateAppearance.java b/src/javax/media/j3d/AlternateAppearance.java new file mode 100644 index 0000000..48b9066 --- /dev/null +++ b/src/javax/media/j3d/AlternateAppearance.java @@ -0,0 +1,619 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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). + * + *

+ * 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. + * + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_INFLUENCING_BOUNDS_READ, + ALLOW_APPEARANCE_READ, + ALLOW_SCOPE_READ + }; + + /** + * Constructs an AlternateAppearance node with default + * parameters. The default values are as follows: + * + *

+ */ + public AlternateAppearance() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + ((AlternateAppearanceRetained)retained).initAppearance(appearance); + } + + /** + * Creates the retained mode AlternateAppearanceRetained object that this + * Alternate Appearance component object will point to. + */ + @Override + void createRetained() { + this.retained = new AlternateAppearanceRetained(); + this.retained.setSource(this); + } + + /** + * 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 ((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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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(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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + AlternateAppearance app = new AlternateAppearance(); + app.duplicateNode(this, forceDuplicate); + return app; + } +} + diff --git a/src/javax/media/j3d/AlternateAppearanceRetained.java b/src/javax/media/j3d/AlternateAppearanceRetained.java new file mode 100644 index 0000000..636bc13 --- /dev/null +++ b/src/javax/media/j3d/AlternateAppearanceRetained.java @@ -0,0 +1,866 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + + +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((Bounds)null); + } + + /** + * 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(); + ArrayList addScopeList = new ArrayList(); + Object[] scopeInfo = new Object[3]; + + GroupRetained group = 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)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(group, 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 = scopes.remove(index); + group.removeAltAppScope(); + + } + + void removeScope(int index) { + + Object[] scopeInfo = new Object[3]; + ArrayList removeScopeList = new ArrayList(); + GroupRetained group = 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() { + ArrayList removeScopeList = new ArrayList(); + int n = scopes.size(); + for(int index = n-1; index >= 0; index--) { + GroupRetained group = 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((Group)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(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(group); + 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) { + 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 = 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 = 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 (int i = 0; i < addList.size(); i++) { + Shape3DRetained obj = ((GeometryAtom)addList.get(i)).source; + obj.addAltApp(mirrorAltApp); + } + } + + if (removeList != null) { + mirrorAltApp.isScoped = isScoped; + for (int 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 + */ + + @Override + 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; + } + } + } + + @Override + void setLive(SetLiveState s) { + + 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 = 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 = new J3dMessage(); + 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 (int i = 0; i < scopes.size(); i++) { + GroupRetained group = 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 + */ + @Override + 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 = new J3dMessage(); + 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 = scopes.get(i); + tempKey.reset(); + group.removeAllNodesForScopedAltApp(mirrorAltApp, removeScopeList, tempKey); + } + createMessage.args[2] = removeScopeList; + VirtualUniverse.mc.processMessage(createMessage); + } + } + + + + @Override + 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 = new J3dMessage(); + 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); + } + + + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + leafList.add(mirrorAltApp); + } + + + /** + * Copies all AlternateAppearance information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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; + +// // XXXX: 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 cloneTree. +// * This method is called by cloneTree 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 getNewObjectReference method found in the +// * NodeReferenceTable 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 +// * allowDanglingReferences parameter passed in the +// * cloneTree call. +// *

+// * NOTE: Applications should not call this method directly. +// * It should only be called by the cloneTree method. +// * +// * @param referenceTable a NodeReferenceTableObject that contains the +// * getNewObjectReference 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/javax/media/j3d/AmbientLight.java b/src/javax/media/j3d/AmbientLight.java new file mode 100644 index 0000000..588ea51 --- /dev/null +++ b/src/javax/media/j3d/AmbientLight.java @@ -0,0 +1,106 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * For more information on Java 3D lighting, see the class description + * for Light. + *

+ */ + +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. + */ + @Override + void createRetained() { + this.retained = new AmbientLightRetained(); + this.retained.setSource(this); + } + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + AmbientLight a = new AmbientLight(); + a.duplicateNode(this, forceDuplicate); + return a; + } + +} diff --git a/src/javax/media/j3d/AmbientLightRetained.java b/src/javax/media/j3d/AmbientLightRetained.java new file mode 100644 index 0000000..dcc7c22 --- /dev/null +++ b/src/javax/media/j3d/AmbientLightRetained.java @@ -0,0 +1,52 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * An ambient light source object. + */ + +class AmbientLightRetained extends LightRetained { + + AmbientLightRetained() { + this.nodeType = NodeRetained.AMBIENTLIGHT; + lightType = 1; + localBounds = new BoundingBox((Bounds)null); + } + + @Override + void setLive(SetLiveState s) { + super.setLive(s); + J3dMessage createMessage = super.initMessage(7); + VirtualUniverse.mc.processMessage(createMessage); + } + + @Override + void update(Context ctx, int lightSlot, double scale) { + } +} diff --git a/src/javax/media/j3d/Appearance.java b/src/javax/media/j3d/Appearance.java new file mode 100644 index 0000000..a26eb84 --- /dev/null +++ b/src/javax/media/j3d/Appearance.java @@ -0,0 +1,1032 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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:

+ *

    + *
  • Coloring attributes - defines attributes used in color selection + * and shading. These attributes are defined in a ColoringAttributes + * object.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + * + *

  • Texture - defines the texture image and filtering + * parameters used when texture mapping is enabled. These attributes + * are defined in a Texture object.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + * + *

  • Texture unit state - array that defines texture state for each + * of N 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 n + * -1. If both the old and new values are set, an exception is thrown. + * + *
  • + *
+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_COLORING_ATTRIBUTES_READ, + ALLOW_LINE_ATTRIBUTES_READ, + ALLOW_MATERIAL_READ, + ALLOW_POINT_ATTRIBUTES_READ, + ALLOW_POLYGON_ATTRIBUTES_READ, + ALLOW_RENDERING_ATTRIBUTES_READ, + ALLOW_TEXGEN_READ, + ALLOW_TEXTURE_ATTRIBUTES_READ, + ALLOW_TEXTURE_READ, + ALLOW_TEXTURE_UNIT_STATE_READ, + ALLOW_TRANSPARENCY_ATTRIBUTES_READ + }; + + /** + * 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 + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Creates the retained mode AppearanceRetained object that this + * Appearance component object will point to. + */ + @Override + 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. + * + *

+ * 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. + * + * @exception IllegalSharingException if this Appearance is live and + * the specified texture refers to an ImageComponent2D that is being used + * by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this Appearance is + * being used by an immediate mode context and + * the specified texture refers to an ImageComponent2D that is being used + * by a Canvas3D as an off-screen buffer. + */ + public void setTexture(Texture texture) { + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_TEXTURE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("Appearance2")); + + // Do illegal sharing check + if(texture != null) { + ImageComponent[] images = ((TextureRetained)(texture.retained)).getImages(); + if(images != null) { + for(int i=0; i + * 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. + * + *

+ * 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. + * + *

+ * 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 0 through + * stateArray.length-1. + * + * @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. + * + * @exception IllegalSharingException if this Appearance is live and + * any of the specified textures refers to an ImageComponent2D that is + * being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this Appearance is + * being used by an immediate mode context and + * any of the specified textures refers to an ImageComponent2D that is + * being used by a Canvas3D as an off-screen buffer. + * + * @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")); + + // Do illegal sharing check + if (stateArray != null) { + for(int j=0; j[0, stateArray.length-1]. + * + * @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 index >= + * stateArray.length. + * + * @exception IllegalSharingException if this Appearance is live and + * the specified texture refers to an ImageComponent2D that is being used + * by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this Appearance is + * being used by an immediate mode context and + * the specified texture refers to an ImageComponent2D that is being used + * by a Canvas3D as an off-screen buffer. + * + * @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")); + + // Do illegal sharing check + if (state != null) { + TextureRetained texRetained = + ((TextureUnitStateRetained)state.retained).texture; + if(texRetained != null) { + ImageComponent[] images = texRetained.getImages(); + if(images != null) { + for(int i=0; i[0, stateArray.length-1]. + * + * @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) + */ + @Override + public NodeComponent cloneNodeComponent() { + Appearance a = new Appearance(); + a.duplicateNodeComponent(this); + return a; + } + + /** + * 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) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } + + /** + * Copies all Appearance information from + * originalNodeComponent into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNodeComponent the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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. + */ + @Override + 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; + + // XXXX: TextureUnitState + + return false; + } + +} diff --git a/src/javax/media/j3d/AppearanceRetained.java b/src/javax/media/j3d/AppearanceRetained.java new file mode 100644 index 0000000..987e911 --- /dev/null +++ b/src/javax/media/j3d/AppearanceRetained.java @@ -0,0 +1,1413 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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_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 && stateArray.length > 0) { + + 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] = 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; + } + + + @Override + 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. + */ + @Override + synchronized void initMirrorObject() { + + AppearanceRetained mirrorApp = (AppearanceRetained)mirror; + + mirrorApp.source = source; + mirrorApp.sgApp = this; + + // Fix for Issue 33: copy the changedFrequent mask to mirror + mirrorApp.changedFrequent = changedFrequent; + + 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" + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + AppearanceRetained mirrorApp = (AppearanceRetained)mirror; + if ((component & MATERIAL) != 0) { + mirrorApp.material = (MaterialRetained)value; + } + else if ((component & TEXTURE) != 0) { + // Issue 435: set mirror texture + mirrorApp.texture = (TextureRetained)value; + 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; + } + + } + + @Override + void setLive(boolean backgroundGroup, int refCount) { + // System.err.println("AppearceRetained.setLive()"); + doSetLive(backgroundGroup, refCount); + markAsLive(); + } + + /** + * This method calls the setLive method of all appearance bundle + * objects. + */ + @Override + void doSetLive(boolean backgroundGroup, int refCount) { + // System.err.println("AppearceRetained.doSetLive()"); + + 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); + } + + /** + * This method calls the clearLive method of all appearance bundle + * objects. + */ + @Override + 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); + } + } + + + @Override + 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; + } + + // Issue 209 - enable this method (was previously commented out) + // Simply pass along to the NodeComponents + @Override + 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. + */ + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + 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); + } + + + + + @Override + 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); + } + + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + VirtualUniverse.mc.processMessage(createMessage); + } + } + + boolean isOpaque(int geoType) { + if (TransparencyAttributesRetained.useAlpha(transparencyAttributes)) + 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); + } + + @Override + 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/javax/media/j3d/AssertionFailureException.java b/src/javax/media/j3d/AssertionFailureException.java new file mode 100644 index 0000000..2135e12 --- /dev/null +++ b/src/javax/media/j3d/AssertionFailureException.java @@ -0,0 +1,49 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/AttributeBin.java b/src/javax/media/j3d/AttributeBin.java new file mode 100644 index 0000000..95ce52a --- /dev/null +++ b/src/javax/media/j3d/AttributeBin.java @@ -0,0 +1,418 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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 RenderBin for this object + */ + RenderBin renderBin = 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 ShaderBins in this AttributeBin + */ + ShaderBin shaderBinList = null; + +/** + * List of shaderBins to be added next frame + */ +ArrayList addShaderBins = 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; + + 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; + + // XXXX: use definingMaterial etc. instead of these + // when sole user is completely implement + RenderingAttributesRetained renderingAttrs; + + int numEditingShaderBins = 0; + + AttributeBin(AppearanceRetained app, RenderingAttributesRetained renderingAttributes, RenderBin rBin) { + + reset(app, renderingAttributes, rBin); + } + + void reset(AppearanceRetained app, RenderingAttributesRetained renderingAttributes, RenderBin rBin) { + prev = null; + next = null; + shaderBinList = null; + onUpdateList = 0; + numEditingShaderBins = 0; + renderingAttrs = renderingAttributes; + + renderBin = rBin; + + // Issue 249 - check for sole user only if property is set + soleUser = false; + if (VirtualUniverse.mc.allowSoleUser) { + if (app != null) { + soleUser = ((app.changedFrequent & AppearanceRetained.RENDERING) != 0); + } + } + + //System.err.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 ) { + renderBin.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 (soleUser || (ra.geometryAtom.source.appearance != null && + ((ra.geometryAtom.source.appearance.changedFrequent & + AppearanceRetained.RENDERING) != 0))) { + if (app == 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 (numEditingShaderBins == 0) { + if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0) { + 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 ) { + 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); + } + + @Override + public void updateObject() { + ShaderBin sb; + int i, size; + + size = addShaderBins.size(); + if (size > 0) { + sb = addShaderBins.get(0); + if (shaderBinList == null) { + shaderBinList = sb; + } + else { + sb.next = shaderBinList; + shaderBinList.prev = sb; + shaderBinList = sb; + } + + for (i = 1; i < size ; i++) { + sb = addShaderBins.get(i); + sb.next = shaderBinList; + shaderBinList.prev = sb; + shaderBinList = sb; + } + } + addShaderBins.clear(); + onUpdateList &= ~ON_OBJ_UPDATE_LIST; + } + + + /** + * Adds the given shaderBin to this AttributeBin. + */ + void addShaderBin(ShaderBin sb, RenderBin rb, ShaderAppearanceRetained sApp) { + + sb.attributeBin = this; + + if(sApp != null) { + // ShaderBin should reference to the mirror components. -- JADA. + // System.err.println("AttributeBin : sApp.isMirror = " + sApp.isMirror); + assert(sApp.isMirror); + sb.shaderProgram = sApp.shaderProgram; + sb.shaderAttributeSet = sApp.shaderAttributeSet; + } + sb.shaderAppearance = sApp; + + // TODO : JADA - sort by ShaderProgram to avoid state trashing. + addShaderBins.add(sb); + if ((onUpdateList & ON_OBJ_UPDATE_LIST) == 0) { + onUpdateList |= ON_OBJ_UPDATE_LIST; + rb.objUpdateList.add(this); + } + + } + + + /** + * Removes the given shaderBin from this AttributeBin. + */ + void removeShaderBin(ShaderBin sb) { + + // If the shaderBin being remove is contained in addShaderBins, + // then remove the shadereBin from the addList + if (addShaderBins.contains(sb)) { + addShaderBins.remove(addShaderBins.indexOf(sb)); + } + else { + if (sb.prev == null) { // At the head of the list + shaderBinList = sb.next; + if (sb.next != null) { + sb.next.prev = null; + } + } else { // In the middle or at the end. + sb.prev.next = sb.next; + if (sb.next != null) { + sb.next.prev = sb.prev; + } + } + } + + sb.clear(); + + if (shaderBinList == null && addShaderBins.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) { + + ShaderBin sb; + + boolean visible = (definingRenderingAttributes == null || + definingRenderingAttributes.visible); + + if ( (renderBin.view.viewCache.visibilityPolicy + == View.VISIBILITY_DRAW_VISIBLE && !visible) || + (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); + + sb = shaderBinList; + while (sb != null) { + sb.render(cv); + sb = sb.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, + 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, + 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 incrActiveShaderBin() { + numEditingShaderBins++; + } + + void decrActiveShaderBin() { + numEditingShaderBins--; + } + + void updateFromShaderBin(RenderAtom ra) { + + AppearanceRetained raApp = ra.geometryAtom.source.appearance; + RenderingAttributesRetained rAttrs = + (raApp == null)? null : raApp.renderingAttributes; + + if (!soleUser && renderingAttrs != rAttrs) { + // no longer sole user + renderingAttrs = definingRenderingAttributes; + } + } +} diff --git a/src/javax/media/j3d/AudioDevice.java b/src/javax/media/j3d/AudioDevice.java new file mode 100644 index 0000000..1aab9fc --- /dev/null +++ b/src/javax/media/j3d/AudioDevice.java @@ -0,0 +1,271 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The AudioDevice Class defines and encapsulates the + * audio device's basic information and characteristics. + *

+ * 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. + *

+ * AudioDevice Interface + *

+ *

    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. + *

    + * 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. + *

    + *Initialization + *

      + * 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.
    + *

    + * Audio Playback Type + *

      + * 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.
    + *

    + * Distance to Speaker + *

      + * 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.
    + *

    + * Angular Offset of Speakers + *

      + * 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).
    + *

    + * Device Driver Specific Data + *

      + * 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.
+ *

+ * Instantiating and Registering a New Device + *

+ *

    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. + *

    + * 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. + *

    + * 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.

+ *

+ * 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. + *

+ * If an application creates it's own instance of an AudioDevice and + * initializes it directly, rather than using PhysicalEnvironment. + * setAudioDevice(), that application may make any 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/javax/media/j3d/AudioDevice3D.java b/src/javax/media/j3d/AudioDevice3D.java new file mode 100644 index 0000000..7f152e4 --- /dev/null +++ b/src/javax/media/j3d/AudioDevice3D.java @@ -0,0 +1,533 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point2f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector3f; + + +/** + * The AudioDevice3D class defines a 3D audio device that is used to set + * sound and aural attributes. + *

+ * 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. + *

+ * The intent is for this interface to be implemented by AudioDevice Driver + * developers using a software or hardware sound engine of their choice. + *

+ * 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: + *

    + * Set up and clear the sound as a sample on the device. + *

    + * Start, stop, pause, unpause, mute, and unmute of sample on the device. + *

    + * Set parameters for each sample corresponding to the fields in the + * Sound node. + *

    + * Set the current active aural parameters that affect all positional samples. + *

+ *

+ * Sound Types + *

+ * 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. + * + *

+ * Sound Data Types + *

+ * 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). + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * For some AudioDevice3D implementations: + *

    + * Muted sound take channels up on the systems mixer (because they're + * rendered as samples playing with gain zero. + *

    + * A single sound could be rendered using multiple samples, each taking + * up mixer channels. + *

+ *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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). + *

+ * 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). + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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/javax/media/j3d/AudioDevice3DL2.java b/src/javax/media/j3d/AudioDevice3DL2.java new file mode 100644 index 0000000..337328c --- /dev/null +++ b/src/javax/media/j3d/AudioDevice3DL2.java @@ -0,0 +1,319 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * The reverberation methods of AudioDevice3DL2 interface augment the + * reverberation methods defined in AudioDevice3D. + *

+ * The intent is for this interface to be implemented by AudioDevice Driver + * developers using a software or hardware sound engine of their choice. + *

+ * 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. + *

+ * 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). + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * A full description of this parameter and how it is used is in + * the documentation for the AuralAttributes class. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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/javax/media/j3d/AudioDeviceEnumerator.java b/src/javax/media/j3d/AudioDeviceEnumerator.java new file mode 100644 index 0000000..dbab9b3 --- /dev/null +++ b/src/javax/media/j3d/AudioDeviceEnumerator.java @@ -0,0 +1,83 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + */ + @Override + 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 + */ + @Override + public Object nextElement() { + if (this.hasMoreElements()) { + endOfList = true; + return ((Object) device); + } else { + throw new NoSuchElementException(J3dI18N.getString("AudioDeviceEnumerator0")); + } + } +} diff --git a/src/javax/media/j3d/AuralAttributes.java b/src/javax/media/j3d/AuralAttributes.java new file mode 100644 index 0000000..563bc3a --- /dev/null +++ b/src/javax/media/j3d/AuralAttributes.java @@ -0,0 +1,1307 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * Attribute Gain + *

    + * 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 + *
+ *

+ * Attribute Gain Rolloff + *

    + * 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). + *
+ *

+ * Auralization

+ *

    + * 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. + *

    + * In Java3D's model for auralization there are several parameters + * that approximate sound reflection and reverberation for a particular + * listening space: + *

      Reflection Coefficient
        Gain attenuation of the initial + * reflections across all frequencies.
    + *
      (Early) Reflection Delay
        The time it takes for the first + * low-order reflected sound to reach the listener.
    + *
      Reverb Coefficient
        Gain attenuation of the late reflections + * (referred to as 'reverb') across all frequencies.
    + *
      Reverb Delay
        The time it takes for reverbered sound + * to reach the listener.
    + *
      Decay Time
        Describes the reverb decay curve by defining the + * length of time reverb takes to decay to effective zero. + *
    + *
      Decay Filter
        High-frequencies of the late reverberation + * can be attenuated at a different rate.
    + *
      Density
        Modal density (spectral coloration) of + * reverberation.
    + *
      Diffusion
        Echo dispersement of reverberation.
    + *
      Reverb Bounds
        Approximates the volume of the listening space. + * If specified, it defines the reverberation delay.
    + *
      Reverb Order
        Optionally limits the amount of times during + * reverb calculation that a sound is recursively reflected off the + * bounding region.
    + *

    + * Reflection Coefficient + *

      + * 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). + *
    + *

    + * Reflection Delay + *

      + * 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. + *
    + *

    + * Reverberation Coefficient + *

      + * 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). + *
    + *

    + * Reverberation Delay + *

      + * 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. + *
    + *

    + * Reverberation Bounds + *

      + * 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. + *
    + *

    + * Reverberation Order + *

      + * 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. + *
    + *

    + * Decay Time + *

      + * 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. + *
    + *

    + * Decay Filter + *

      + * 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. + *
    + *

    + * Reverberation Diffusion + *

      + * 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). + *
    + *

    + * Reverberation Density + *

      + * 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. + *
    + *
+ *

+ * Distance Filter + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * The getDistanceFilterLength method returns the length of the distance filter + * arrays. Arrays passed into getDistanceFilter methods should all be at + * least this size.

+ *

+ * Doppler Effect Model + *

    + * 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. + *

    + * 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. + *

    + * 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.

+ */ +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_ATTRIBUTE_GAIN_READ, + ALLOW_DECAY_FILTER_READ, + ALLOW_DECAY_TIME_READ, + ALLOW_DENSITY_READ, + ALLOW_DIFFUSION_READ, + ALLOW_DISTANCE_FILTER_READ, + ALLOW_FREQUENCY_SCALE_FACTOR_READ, + ALLOW_REFLECTION_COEFFICIENT_READ, + ALLOW_REFLECTION_DELAY_READ, + ALLOW_REVERB_COEFFICIENT_READ, + ALLOW_REVERB_DELAY_READ, + ALLOW_REVERB_ORDER_READ, + ALLOW_ROLLOFF_READ, + ALLOW_VELOCITY_SCALE_FACTOR_READ + }; + + /** ***************** + * + * Constructors + * + * ******************/ + /** + * Constructs and initializes a new AuralAttributes object using default + * parameters. The following default values are used: + *
    + * attribute gain: 1.0
    + * rolloff: 1.0
    + * reflection coeff: 0.0
    + * reflection delay: 20.0
    + * reverb coeff: 1.0
    + * reverb delay: 40.0
    + * decay time: 1000.0
    + * decay filter: 5000.0<> + * diffusion: 1.0
    + * density: 1.0
    + * reverb bounds: null
    + * reverb order: 0
    + * distance filtering: null (no filtering performed)
    + * frequency scale factor: 1.0
    + * velocity scale factor: 0.0
    + *
+ */ + public AuralAttributes() { + // Just use default values + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 + * setReverbBounds(Bounds) + */ + 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. + * @param 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 + * cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + AuralAttributes a = new AuralAttributes(); + a.duplicateNodeComponent(this, this.forceDuplicate); + return a; + } + + + /** + * Copies all AuralAttributes information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/AuralAttributesRetained.java b/src/javax/media/j3d/AuralAttributesRetained.java new file mode 100644 index 0000000..f5e3d6a --- /dev/null +++ b/src/javax/media/j3d/AuralAttributesRetained.java @@ -0,0 +1,667 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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.err.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 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"); + // XXXX: (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/javax/media/j3d/AutoOffScreenCanvas3D.java b/src/javax/media/j3d/AutoOffScreenCanvas3D.java new file mode 100644 index 0000000..0fc0a60 --- /dev/null +++ b/src/javax/media/j3d/AutoOffScreenCanvas3D.java @@ -0,0 +1,21 @@ +/* + * Copyright 2013 Harvey Harrison + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + */ +package javax.media.j3d; + +/** + * An interface used to tag Canavs3d subclasses as being offsceen. + */ +public interface AutoOffScreenCanvas3D {} diff --git a/src/javax/media/j3d/BHInsertStructure.java b/src/javax/media/j3d/BHInsertStructure.java new file mode 100644 index 0000000..b64bfb5 --- /dev/null +++ b/src/javax/media/j3d/BHInsertStructure.java @@ -0,0 +1,154 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Random; + +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= bhListArrMaxCnt) { + // allocate a bigger array here.... + if(debug) + System.err.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.err.println("child1 or child2 is null ..."); + System.err.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/javax/media/j3d/BHInternalNode.java b/src/javax/media/j3d/BHInternalNode.java new file mode 100644 index 0000000..1272e58 --- /dev/null +++ b/src/javax/media/j3d/BHInternalNode.java @@ -0,0 +1,209 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +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); + } + + @Override + 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); + + } + + @Override + 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.err.println("Incorrect use of insertion, current node"); + System.err.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); + } + } + + @Override + void destroyTree(BHNode[] bhArr, int[] index) { + + if(rChild != null) + rChild.destroyTree(bhArr, index); + + if(lChild != null) + lChild.destroyTree(bhArr, index); + + rChild = null; + lChild = null; + } +} + diff --git a/src/javax/media/j3d/BHLeafInterface.java b/src/javax/media/j3d/BHLeafInterface.java new file mode 100644 index 0000000..195169f --- /dev/null +++ b/src/javax/media/j3d/BHLeafInterface.java @@ -0,0 +1,40 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/BHLeafNode.java b/src/javax/media/j3d/BHLeafNode.java new file mode 100644 index 0000000..2fcedbb --- /dev/null +++ b/src/javax/media/j3d/BHLeafNode.java @@ -0,0 +1,107 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +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; + } + + @Override + void computeBoundingHull() { + bHull = leafIF.computeBoundingHull(); + } + + @Override + 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(); + } + + @Override + void destroyTree(BHNode[] bhArr, int[] index) { + if(bhArr.length <= index[0]) { + // System.err.println("BHLeafNode : Problem bhArr overflow!!!"); + return; + } + + parent = null; + bhArr[index[0]] = this; + index[0]++; + } + +} diff --git a/src/javax/media/j3d/BHNode.java b/src/javax/media/j3d/BHNode.java new file mode 100644 index 0000000..23c4a93 --- /dev/null +++ b/src/javax/media/j3d/BHNode.java @@ -0,0 +1,281 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +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.err.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.err.println("BHNode.java: Trouble! No match found. This can't happen."); + System.err.println("this " + this ); + if ( this.nodeType == BHNode.BH_TYPE_INTERNAL) { + System.err.println("rChild " + ((BHInternalNode)this).rChild + + " lChild " + ((BHInternalNode)this).lChild); + } + System.err.println("parent " + parent + + " parent.rChild " + parent.rChild + + " parent.lChild " + parent.lChild); + } + } + } + } + + // 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.err.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/javax/media/j3d/BHTree.java b/src/javax/media/j3d/BHTree.java new file mode 100644 index 0000000..260448c --- /dev/null +++ b/src/javax/media/j3d/BHTree.java @@ -0,0 +1,1154 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Vector; + +import javax.vecmath.Point4d; + +class BHTree { + + Locale locale; + + private BHNode root; + private 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= 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 = parents.get(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.err.println("Length of array is " + bhArr.length); + for(int kk=0; kk depthUpperBound) { + int maxDepth = root.computeMaxDepth(0); + int leafCount = root.countNumberOfLeaves(); + double compDepth = Math.log(leafCount)/LOG_OF_2; + /* + System.err.println("BHTree - evaluate for reConstructTree ..."); + System.err.println("compDepth " + compDepth); + System.err.println("maxDepth " + maxDepth); + System.err.println("leafCount " + leafCount); + */ + + // Upper bound guard. + if(maxDepth > depthUpperBound) { + reConstructTree(leafCount); + maxDepth = root.computeMaxDepth(0); + /* + System.err.println("BHTree - Did reConstructTree ..."); + System.err.println("compDepth " + compDepth); + System.err.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=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.err.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 = new BHInternalNode(); + 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 = new BHInternalNode(); + 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.err.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.err.println("Statistics for tree = " + this); + System.err.println("Total Number of nodes in tree = " + + (leafCount + internalCount) ); + System.err.println("Number of Leaf Nodes = " + leafCount ); + System.err.println("Number of Internal Nodes = " + internalCount ); + System.err.println("Maximum Leaf depth = " + maxDepth ); + System.err.println("Average Leaf depth = " + averageDepth ); + System.err.println("root.bHull = " + root.bHull); + // printTree(root); + + } + + + void printTree(BHNode bh) { + if(bh!= null) { + if(bh.nodeType == BHNode.BH_TYPE_INTERNAL) { + System.err.println("BH_TYPE_INTERNAL - bHull : " + bh); + System.err.println(bh.bHull); + System.err.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.err.println("BH_TYPE_LEAF - bHull : " + bh); + System.err.println(bh.bHull); + } + + } + + + } +} + + + + + + diff --git a/src/javax/media/j3d/Background.java b/src/javax/media/j3d/Background.java new file mode 100644 index 0000000..e7cf50b --- /dev/null +++ b/src/javax/media/j3d/Background.java @@ -0,0 +1,789 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Color3f; + +/** + * 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. + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * 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 window.width/image.width + * and window.height/image.height. 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 window.width/image.width + * and window.height/image.height. The image will + * entirely fill the window, but may by clipped either in X + * or Y. + * 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 x and + * y by window.width/image.width and and + * window.height/image.height, 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_APPLICATION_BOUNDS_READ, + ALLOW_COLOR_READ, + ALLOW_GEOMETRY_READ, + ALLOW_IMAGE_READ, + ALLOW_IMAGE_SCALE_MODE_READ + }; + + + /** + * Constructs a Background node with default parameters. The default + * values are as follows: + *

    + * color : black (0,0,0)
    + * image : null
    + * geometry : null
    + * image scale mode : SCALE_NONE
    + * application bounds : null
    + * application bounding leaf : null
    + *
+ */ + public Background () { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is ImageClass.NIO_IMAGE_BUFFER. + */ + public Background(ImageComponent2D image) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + if((image != null) && + (image.getImageClass() == ImageComponent.ImageClass.NIO_IMAGE_BUFFER)) { + throw new IllegalArgumentException(J3dI18N.getString("Background14")); + } + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * + * @exception IllegalSharingException if this Background is live and + * the specified image is being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this Background is + * being used by an immediate mode context and + * the specified image is being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is ImageClass.NIO_IMAGE_BUFFER. + */ + public void setImage(ImageComponent2D image) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_IMAGE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("Background3")); + + BackgroundRetained bgRetained = (BackgroundRetained)this.retained; + + if((image != null) && + (image.getImageClass() == ImageComponent.ImageClass.NIO_IMAGE_BUFFER)) { + throw new IllegalArgumentException(J3dI18N.getString("Background14")); + } + + // Do illegal sharing check + if(image != null) { + ImageComponent2DRetained imageRetained = (ImageComponent2DRetained) image.retained; + if(imageRetained.getUsedByOffScreen()) { + if(isLive()) { + throw new IllegalSharingException(J3dI18N.getString("Background12")); + } + if(bgRetained.getInImmCtx()) { + throw new IllegalSharingException(J3dI18N.getString("Background13")); + } + } + } + + if (isLive()) + bgRetained.setImage(image); + else + bgRetained.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 imageScaleMode + * 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. + */ + @Override + void createRetained() { + this.retained = new BackgroundRetained(); + this.retained.setSource(this); + } + + + /** + * Creates a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied.
+ * Background geometry will not clone in this operation. + * It is the user's responsibility + * to call cloneTree on that branchGroup. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Background b = new Background(); + b.duplicateNode(this, forceDuplicate); + return b; + } + + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + * + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * Background + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean + forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + + /** + * Copies all Background information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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()); + // issue # 563: add call to cloneTree() + rt.initGeometry((BranchGroup) (attr.getGeometry() == null ? null : attr.getGeometry().cloneTree(true))); + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances + * + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/BackgroundRetained.java b/src/javax/media/j3d/BackgroundRetained.java new file mode 100644 index 0000000..847d8de --- /dev/null +++ b/src/javax/media/j3d/BackgroundRetained.java @@ -0,0 +1,782 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3f; + + +/** + * 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; + Texture2DRetained texture = 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; + + BackgroundRetained () { + this.nodeType = NodeRetained.BACKGROUND; + localBounds = new BoundingBox((Bounds)null); + } + + /** + * 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 ImageCompoent2D object used as the background image + */ + final void initImage(ImageComponent2D img) { + int texFormat; + + if (img == null) { + image = null; + texture = null; + return; + } + + if (img.retained != image ) { + image = (ImageComponent2DRetained) img.retained; + image.setEnforceNonPowerOfTwoSupport(true); + switch(image.getNumberOfComponents()) { + case 1: + texFormat = Texture.INTENSITY; + break; + case 2: + texFormat = Texture.LUMINANCE_ALPHA; + break; + case 3: + texFormat = Texture.RGB; + break; + case 4: + texFormat = Texture.RGBA; + break; + default: + assert false; + return; + } + + Texture2D tex2D = new Texture2D(Texture.BASE_LEVEL, texFormat, + img.getWidth(), img.getHeight()); + texture = (Texture2DRetained) tex2D.retained; + // Background is special case of Raster. + texture.setUseAsRaster(true); + // Fix to issue 373 : ImageComponent.set(BufferedImage) ignored when used by Background + image.addUser(texture); + texture.initImage(0,img); + } + } + + /** + * 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 (texture != null) { + texture.clearLive(refCount); + } + } + initImage(img); + if (source.isLive()) { + if (texture != null) { + texture.setLive(inBackgroundGroup, refCount); + } + + sendMessage(IMAGE_CHANGED, + (texture != null ? texture.mirror : 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(); + 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 + */ + @Override + void setLive(SetLiveState s) { + 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.err.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 = s.switchStates.get(0); + + // Initialize some mirror values + if (boundingLeaf != null) { + transformedRegion = 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 (texture != null) { + texture.setLive(inBackgroundGroup, refCount); + } + super.markAsLive(); + + } + + /** + * This clearLive routine first calls the superclass's method, then + * it removes itself to the list of lights + */ + @Override + 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(branch); + // 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 (texture != null) { + texture.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(); + // If initialization + + // 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( + (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 + */ + @Override + 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 = new J3dMessage(); + 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(geomAtom); + bgGeometryAtomListDirty = true; +} + +GeometryAtom[] getBackgroundGeometryAtoms() { + if (bgGeometryAtomListDirty) { + int nAtoms = bgGeometryAtomList.size(); + if (nAtoms == 0) { + bgGeometryAtoms = null; + } + else { + bgGeometryAtoms = bgGeometryAtomList.toArray(new GeometryAtom[nAtoms]); + bgGeometryAtomListDirty = false; + } + } + return bgGeometryAtoms; +} + + @Override + 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) { + } + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + leafList.add(this); // No Mirror in this case + } +} diff --git a/src/javax/media/j3d/BackgroundSound.java b/src/javax/media/j3d/BackgroundSound.java new file mode 100644 index 0000000..8ca0de0 --- /dev/null +++ b/src/javax/media/j3d/BackgroundSound.java @@ -0,0 +1,153 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + void createRetained() { + this.retained = new BackgroundSoundRetained(); + this.retained.setSource(this); + } + + + /** + * Creates a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + BackgroundSound b = new BackgroundSound(); + b.duplicateNode(this, forceDuplicate); + return b; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + * + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * Sound + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } +} diff --git a/src/javax/media/j3d/BackgroundSoundRetained.java b/src/javax/media/j3d/BackgroundSoundRetained.java new file mode 100644 index 0000000..6f026f6 --- /dev/null +++ b/src/javax/media/j3d/BackgroundSoundRetained.java @@ -0,0 +1,40 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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((Bounds)null); + } +} diff --git a/src/javax/media/j3d/BadTransformException.java b/src/javax/media/j3d/BadTransformException.java new file mode 100644 index 0000000..c7c2c5c --- /dev/null +++ b/src/javax/media/j3d/BadTransformException.java @@ -0,0 +1,69 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + *

    + *
  • + * 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. + *
  • + * 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. + *
  • + * Most viewing transforms other than those in the scene graph can + * only contain translation and rotation. + *
  • + * The projection transform is allowed to be non-affine, but it + * must either be a single point perspective projection or a parallel + * projection. + *
+ */ +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/javax/media/j3d/Behavior.java b/src/javax/media/j3d/Behavior.java new file mode 100644 index 0000000..f0e5501 --- /dev/null +++ b/src/javax/media/j3d/Behavior.java @@ -0,0 +1,525 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + * initialization method, called once when the behavior + * becomes "live," and a processStimulus 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. + * + *

+ * The scheduling region defines a spatial volume that serves + * to enable the scheduling of Behavior nodes. A Behavior node is + * active (can receive stimuli) whenever an active ViewPlatform's + * activation volume intersects a Behavior object's scheduling + * region. Only active behaviors can receive stimuli. + * + *

+ * 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 initialize 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. + * + *

+ * The processStimulus 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: + *

    + *
  1. All modifications to scene graph objects (not including geometry + * by-reference or texture by-reference) made from the + * processStimulus method of a single behavior instance + * are guaranteed to take effect in the same rendering frame.
  2. + *
  3. All modifications to scene graph objects (not including geometry + * by-reference or texture by-reference) made from the + * processStimulus 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.
  4. + *
+ * + * 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. + * + *

+ * Code Structure + *

+ * 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. + * + *

+ * 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. + * + *

+ * 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: + * + *

+ *

    + *
  • Code to decode and extract references from the WakeupCondition + * enumeration that caused the object's awakening.
  • + *
  • Code to perform the manipulations associated with the + * WakeupCondition
  • + *
  • Code to establish this behavior's new WakeupCondition
  • + *
  • A path to Exit (so that execution returns to the Java 3D + * behavior scheduler)
  • + *
+ * + *

+ * WakeupCondition Object + *

+ * 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. + * + *

+ * WakeupCriterion Object + *

+ * 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 + * + *

    + *
  • The center of a ViewPlatform enters a specified region
  • + *
  • The center of a ViewPlatform exits a specified region
  • + *
  • A behavior is activated
  • + *
  • A behavior is deactivated
  • + *
  • A specified TransformGroup node's transform changes
  • + *
  • Collision is detected between a specified Shape3D node's + * Geometry object and any other object
  • + *
  • Movement occurs between a specified Shape3D node's Geometry + * object and any other object with which it collides
  • + *
  • A specified Shape3D node's Geometry object no longer collides + * with any other object
  • + *
  • A specified Behavior object posts a specific event
  • + *
  • A specified AWT event occurs
  • + *
  • A specified time interval elapses
  • + *
  • A specified number of frames have been drawn
  • + *
  • The center of a specified Sensor enters a specified region
  • + *
  • The center of a specified Sensor exits a specified region
  • + *
+ * + *

+ * 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. + * + *

+ * 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. + * + *

+ * Additional Information + *

+ * For more information, see the + * Introduction to the Java 3D API and + * Behaviors and Interpolators + * documents. + * + * @see WakeupCondition + */ + +public abstract class Behavior extends Leaf { + + /** + * Constructs a Behavior node with default parameters. The default + * values are as follows: + *

    + * enable flag : true
    + * scheduling bounds : null
    + * scheduling bounding leaf : null
    + * scheduling interval : numSchedulingIntervals / 2
    + *
+ */ + public Behavior() { + } + + /** + * Initialize this behavior. Classes that extend Behavior must + * provide their own initialize method. + *
+ * NOTE: Applications should not 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. + *
+ * NOTE: Applications should not 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. + */ + @Override + 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 other than 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. + *

+ * 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. + *

+ * @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 + * numSchedulingIntervals / 2. + * + * @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 numSchedulingIntervals / 2. + * + * @param schedulingInterval the new scheduling interval + * + * @exception IllegalArgumentException if + * schedulingInterval < 0 or + * schedulingInterval >= + * numSchedulingIntervals + * + * @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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/BehaviorRetained.java b/src/javax/media/j3d/BehaviorRetained.java new file mode 100644 index 0000000..b82b09d --- /dev/null +++ b/src/javax/media/j3d/BehaviorRetained.java @@ -0,0 +1,525 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * 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((Bounds)null); + 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. + */ + @Override + 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 = 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. + */ + @Override + 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) { + System.err.println("Exception occurred during Behavior initialization:"); + e.printStackTrace(); + } + catch (Error e) { + // Issue 264 - catch Error + System.err.println("Error 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 = boundingLeaf.mirrorBoundingLeaf.transformedRegion; + } + + + void addWakeupCondition() {} + + final void sendMessage(int mtype, Object arg) { + J3dMessage createMessage = new J3dMessage(); + 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); + } + + @Override + 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/javax/media/j3d/BehaviorScheduler.java b/src/javax/media/j3d/BehaviorScheduler.java new file mode 100644 index 0000000..1a43a34 --- /dev/null +++ b/src/javax/media/j3d/BehaviorScheduler.java @@ -0,0 +1,244 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.logging.Level; + +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); + } + + @Override + 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. + */ + @Override + void doWork(long referenceTime) { + BehaviorRetained arr[]; + UnorderList list; + int i, size, interval; + + lastStartTime = J3dClock.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) { + // Force behavior condition to be unset + // Issue 21: don't call cleanTree here + behavret.conditionSet = false; + System.err.println("Exception occurred during Behavior execution:"); + e.printStackTrace(); + } + catch (Error e) { + // Force behavior condition to be unset + // Fix for issue 264 + behavret.conditionSet = false; + System.err.println("Error 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 = J3dClock.currentTimeMillis(); + + if (MasterControl.isStatsLoggable(Level.FINE)) { + VirtualUniverse.mc.recordTime(MasterControl.TimeType.BEHAVIOR, (lastStopTime-lastStartTime)*1000000); + } + } + + 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/javax/media/j3d/BehaviorStructure.java b/src/javax/media/j3d/BehaviorStructure.java new file mode 100644 index 0000000..dc10a13 --- /dev/null +++ b/src/javax/media/j3d/BehaviorStructure.java @@ -0,0 +1,1675 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.AWTEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.Arrays; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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); + + } + + @Override + void processMessages(long referenceTime) { + + J3dMessage[] messages = getMessages(referenceTime); + int nMsg = getNumMessage(); + J3dMessage m; + + if (nMsg > 0) { + for (int i=0; i 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=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 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); + } + } + + } + + @Override + void removeNodes(J3dMessage m) { + Object[] nodes = (Object[]) m.args[0]; + boolean behavRemove = false; + + for (int i=0; i= 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 if ((awtId == MouseEvent.MOUSE_ENTERED) || + (awtId == MouseEvent.MOUSE_EXITED) || + (awtId == MouseEvent.MOUSE_CLICKED) || + (awtId == MouseEvent.MOUSE_PRESSED) || + (awtId == MouseEvent.MOUSE_RELEASED) ) { + mouseEnable = true; + } + else if (awtId == MouseEvent.MOUSE_WHEEL) { + mouseWheelEnable = true; + } + } else { + if ((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0) { + mouseEnable = true; + } + if ((eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0) { + mouseMotionEnable = true; + } + if ((eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0) { + mouseWheelEnable = true; + } + } + } + + if (!focusEnable && universe.enableFocus) { + incTimestamp = true; + universe.disableFocusEvents(); + } + if (!keyEnable && universe.enableKey) { + // key event use for toggle to fullscreen/window mode + incTimestamp = true; + universe.disableKeyEvents(); + } + if (!mouseWheelEnable && universe.enableMouseWheel) { + incTimestamp = true; + universe.disableMouseWheelEvents(); + } + 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) { + // XXXX: 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); + } + else if ((id == MouseEvent.MOUSE_WHEEL) && + (awtCond.EventMask & AWTEvent.MOUSE_WHEEL_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 0) { + ViewPlatformRetained vpArr[] = (ViewPlatformRetained []) + transformViewPlatformList.toArray(false); + + int size = transformViewPlatformList.arraySize(); + for (i=0; i < size; i++) { + processViewPlatformTransform(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 + ViewPlatformRetained[] vpr = universe.getViewPlatformList(); + + physicalEnvironments.clear(); + + for (int i = vpr.length - 1; i >= 0; i--) { + View[] views = vpr[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + View v = views[j]; + if (!v.active) + continue; + + if (!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 Sensor that intersect with behregion or null + */ +Sensor sensorIntersect(Bounds behregion) { + + if (behregion == null) + return null; + + PhysicalEnvironment env[] = (PhysicalEnvironment []) + physicalEnvironments.toArray(false); + for (int i = physicalEnvironments.arraySize() - 1; i >= 0; i--) { + if (env[i].activeViewRef <= 0) + continue; + + Sensor[] sensors = env[i].getSensorList(); + if (sensors == null) + continue; + + for (int j = env[i].users.size() - 1; j >= 0; j--) { + View v = env[i].users.get(j); + synchronized (sensors) { + for (int k = sensors.length - 1; k >= 0; k--) { + Sensor s = sensors[k]; + if (s == null) + continue; + + 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=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=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; + + for (int h=0; h=0; i--) { + + Object[] users = (Object[])obj[i]; + Object[] leafObj = new Object[1]; + 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 = new J3dMessage(); + 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++; + } + } + + } + + @Override + 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/javax/media/j3d/Billboard.java b/src/javax/media/j3d/Billboard.java new file mode 100644 index 0000000..0372062 --- /dev/null +++ b/src/javax/media/j3d/Billboard.java @@ -0,0 +1,675 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Enumeration; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector3f; + +/** + * 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. + * + *

+ * 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. + * + *

+ * Note that in a multiple View system, the alignment is done to + * the primary View only. + * + *

+ * 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: + *

    + * alignment mode : ROTATE_ABOUT_AXIS
    + * alignment axis : Y-axis (0,1,0)
    + * rotation point : (0,0,1)
    + * target transform group: null
    + *
+ */ + 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 Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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 Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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 Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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. + */ + @Override + 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 + */ + @Override + public void processStimulus(Enumeration criteria) { + double angle = 0.0; + double sign; + + 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 = v.getCanvas3D(0); + boolean status; + + Transform3D xform = new Transform3D(); + Transform3D bbXform = new Transform3D(); + Transform3D prevTransform = new Transform3D(); + + ((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 = new Transform3D(); + + // 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); + } + } + } + + // Insert wakeup condition into queue + wakeupOn(wakeupFrame); +} + +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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Billboard b = new Billboard(); + b.duplicateNode(this, forceDuplicate); + return b; + } + + + /** + * Copies all Billboard information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/BoundingBox.java b/src/javax/media/j3d/BoundingBox.java new file mode 100644 index 0000000..304e43b --- /dev/null +++ b/src/javax/media/j3d/BoundingBox.java @@ -0,0 +1,1904 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector4d; + +/** + * 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. + */ +final Point3d lower; + +/** + * The corner of the bounding box with the numerically largest values. + */ +final Point3d upper; + private static final double EPS = 1.0E-8; + +/** + * 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 upper 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); + boundsIsEmpty = false; + boundsIsInfinite = false; +} + +/** + * Constructs a BoundingBox from a bounding object. + * @param boundsObject a bounds object + */ +public BoundingBox(Bounds boundsObject) { + boundId = BOUNDING_BOX; + lower = new Point3d(); + upper = new Point3d(); + + if (boundsObject == null || boundsObject.boundsIsEmpty) { + setEmptyBounds(); + return; + } + + if (boundsObject.boundsIsInfinite) { + setInfiniteBounds(); + return; + } + + if( boundsObject.boundId == BOUNDING_BOX){ + BoundingBox box = (BoundingBox)boundsObject; + + lower.set(box.lower); + upper.set(box.upper); + } + else if( boundsObject.boundId == BOUNDING_SPHERE ) { + BoundingSphere sphere = (BoundingSphere)boundsObject; + + lower.set(sphere.center.x - sphere.radius, + sphere.center.y - sphere.radius, + sphere.center.z - sphere.radius); + + upper.set(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.set(-1.0d, -1.0d, -1.0d); + upper.set( 1.0d, 1.0d, 1.0d); + } else { + lower.set(polytope.verts[0]); + upper.set(polytope.verts[0]); + + for(int i=1;i 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) { + boundId = BOUNDING_BOX; + upper = new Point3d(); + lower = new Point3d(); + + if (bounds == null || bounds.length <= 0) { + setEmptyBounds(); + return; + } + + int i = 0; + // find first non empty bounds object + while ((i < bounds.length) && ((bounds[i] == null) || bounds[i].boundsIsEmpty)) { + i++; + } + + if (i >= bounds.length) { + // all bounds objects were empty + setEmptyBounds(); + return; + } + + this.set(bounds[i++]); + if(boundsIsInfinite) + return; + + for(;i 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 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.set(lower); +} + +/** + * Sets the lower corner of this bounding box. + * @param xmin minimum x value of bounding box + * @param ymin minimum y value of bounding box + * @param zmin minimum z value of bounding box + */ +public void setLower(double xmin, double ymin, double zmin) { + lower.set(xmin, ymin, 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.set(p1); + 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.set(upper); +} + +/** + * Sets the upper corner of this bounding box. + * @param xmax max x value of bounding box + * @param ymax max y value of bounding box + * @param zmax max z value of bounding box + */ +public void setUpper(double xmax, double ymax, double zmax) { + upper.set(xmax, ymax, 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.set(p1); + updateBoundsStates(); +} + + /** + * Sets the the value of this BoundingBox + * @param boundsObject another bounds object + */ + @Override + public void set(Bounds boundsObject) { + int i; + + if (boundsObject == null || boundsObject.boundsIsEmpty) { + setEmptyBounds(); + return; + } + + if (boundsObject.boundsIsInfinite) { + setInfiniteBounds(); + return; + } + + 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 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 + */ + @Override + public Object clone() { + return new BoundingBox(this.lower, this.upper); + } + + + /** + * Indicates whether the specified bounds object is + * equal to this BoundingBox object. They are equal if the + * specified bounds object is an instance of + * BoundingBox and all of the data + * members of bounds 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 bounds; + * otherwise false + * + * @since Java 3D 1.2 + */ + @Override + 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 + */ + @Override + public int hashCode() { + long bits = 1L; + bits = J3dHash.mixDoubleBits(bits, lower.x); + bits = J3dHash.mixDoubleBits(bits, lower.y); + bits = J3dHash.mixDoubleBits(bits, lower.z); + bits = J3dHash.mixDoubleBits(bits, upper.x); + bits = J3dHash.mixDoubleBits(bits, upper.y); + bits = J3dHash.mixDoubleBits(bits, upper.z); + return J3dHash.finish(bits); + } + + + /** + * 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 + */ + @Override + 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 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 + */ + @Override + 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) + return; // no non empty bounds so do not modify current bounds + + if(boundsIsEmpty) + this.set(bounds[i++]); + + if(boundsIsInfinite) + return; + + for(;i 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 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 + */ + @Override + 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 + */ + @Override + public void combine(Point3d[] points) { + + int i; + + if( boundsIsInfinite) { + return; + } + + if( boundsIsEmpty) { + this.setUpper(points[0]); + this.setLower(points[0]); + } + + for(i=0;i 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 + */ + @Override + public void transform( Bounds boundsObject, Transform3D matrix) { + + if (boundsObject == null || boundsObject.boundsIsEmpty) { + setEmptyBounds(); + return; + } + + if (boundsObject.boundsIsInfinite) { + setInfiniteBounds(); + return; + } + + if(boundsObject.boundId == BOUNDING_BOX){ + this.set(boundsObject); + this.transform(matrix); + } + else if(boundsObject.boundId == BOUNDING_SPHERE) { + BoundingSphere tmpSphere = new BoundingSphere(boundsObject); + tmpSphere.transform(matrix); + this.set(tmpSphere); + } + else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope tmpPolytope = new 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 + */ + @Override + public void transform(Transform3D matrix) { + + if (boundsIsInfinite) + return; + + Point3d tmpP3d = new Point3d(); + + 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 + */ + @Override + boolean intersect(Point3d origin, Vector3d direction, Point4d position ) { + double t1,t2,tmp,tnear,tfar,invDir,invMag; + double dirx, diry, dirz; + + /* + System.err.println("BoundingBox.intersect(p,d,p) called\n"); + System.err.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.err.println("dir = " + dirx + ", " + diry + ", " + dirz); + System.err.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.err.println("dirx == 0.0"); + if (origin.x < lower.x || origin.x > upper.x ) { + //System.err.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.err.println("x t1 = " + t1 + " t2 = " + t2); + if( t1 > t2) { + tnear = t2; + tfar = t1; + }else { + tnear = t1; + tfar = t2; + } + if( tfar < 0.0 ) { + //System.err.println( "x failed: tnear="+tnear+" tfar="+tfar); + return false; + } + //System.err.println("x tnear = " + tnear + " tfar = " + tfar); + } + // y + if (diry == 0.0) { + //System.err.println("diry == 0.0"); + if( origin.y < lower.y || origin.y > upper.y ){ + //System.err.println( "parallel to y plane and outside"); + return false; + } + } else { + invDir = 1.0/diry; + //System.err.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.err.println("y t1 = " + t1 + " t2 = " + t2); + if( t1 > tnear) tnear = t1; + if( t2 < tfar ) tfar = t2; + + if( (tfar < 0.0) || (tnear > tfar)){ + //System.err.println( "y failed: tnear="+tnear+" tfar="+tfar); + return false; + } + //System.err.println("y tnear = " + tnear + " tfar = " + tfar); + } + + // z + if (dirz == 0.0) { + //System.err.println("dirz == 0.0"); + if( origin.z < lower.z || origin.z > upper.z ) { + //System.err.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.err.println("z t1 = " + t1 + " t2 = " + t2); + if( t1 > tnear) tnear = t1; + if( t2 < tfar ) tfar = t2; + + if( (tfar < 0.0) || (tnear > tfar)){ + //System.err.println( "z failed: tnear="+tnear+" tfar="+tfar); + return false; + } + //System.err.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 + */ + @Override + 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 + */ + @Override + 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.err.println("dirLen is " + dirLen); + invMag = 1.0/dirLen; + dirx = dirx*invMag; + diry = diry*invMag; + dirz = dirz*invMag; + + /* + System.err.println("dir = " + dir); + System.err.println("start = " + start); + System.err.println("lower = " + lower); + System.err.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.err.println("dirx == 0.0"); + if (start.x < lower.x || start.x > upper.x ) { + //System.err.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.err.println("x t1 = " + t1 + " t2 = " + t2); + if( t1 > t2) { + tnear = t2; + tfar = t1; + }else { + tnear = t1; + tfar = t2; + } + if( tfar < 0.0 ) { + //System.err.println( "x failed: tnear="+tnear+" tfar="+tfar); + return false; + } + //System.err.println("x tnear = " + tnear + " tfar = " + tfar); + } + // y + if (diry == 0.0) { + //System.err.println("diry == 0.0"); + if( start.y < lower.y || start.y > upper.y ){ + //System.err.println( "parallel to y plane and outside"); + return false; + } + } else { + invDir = 1.0/diry; + //System.err.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.err.println("y t1 = " + t1 + " t2 = " + t2); + if( t1 > tnear) tnear = t1; + if( t2 < tfar ) tfar = t2; + + if( (tfar < 0.0) || (tnear > tfar)){ + //System.err.println( "y failed: tnear="+tnear+" tfar="+tfar); + return false; + } + //System.err.println("y tnear = " + tnear + " tfar = " + tfar); + } + + // z + if (dirz == 0.0) { + //System.err.println("dirz == 0.0"); + if( start.z < lower.z || start.z > upper.z ) { + //System.err.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.err.println("z t1 = " + t1 + " t2 = " + t2); + if( t1 > tnear) tnear = t1; + if( t2 < tfar ) tfar = t2; + + if( (tfar < 0.0) || (tnear > tfar)){ + //System.err.println( "z failed: tnear="+tnear+" tfar="+tfar); + return false; + } + //System.err.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.err.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.err.println("tnear = " + tnear + " tfar = " + tfar + " w " + + position.w); + System.err.println("lower = " + lower); + System.err.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 + */ + @Override + 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 + */ + @Override + 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 not empty. + * @return true if the bounding box is empty; otherwise, it returns false + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + public boolean intersect(Bounds[] boundsObjects) { + + 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.err.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 ) { + newBoundBox.set((Bounds)null); + 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.set((Bounds)null); + 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.set((Bounds)null); + return false; + } + + // System.err.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.set((Bounds)null); + 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.set((Bounds)null); + 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.set((Bounds)null); + return false; + } + + + boolean status = false; + BoundingBox tbox = new BoundingBox(); + + for(;i 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.set((Bounds)null); + } + return status; + } + + + /** + * Finds closest bounding object that intersects this bounding box. + * @param boundsObjects an array of bounds objects + * @return closest bounding object + */ + @Override + public Bounds closestIntersection( Bounds[] boundsObjects) { + + if( boundsObjects == null || boundsObjects.length <= 0 ) { + return null; + } + + if( boundsIsEmpty ) { + return null; + } + + Point3d centroid = getCenter(); + + double dis; + 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 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.err.println("intersect frustum with box="+this.toString()); + // System.err.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) ) { + + // System.err.println("*** box and bounding box of frustum do not intersect"); + return false; + } + + // 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 ) < 0.0 ) && + (( upper.x*vc.x + lower.y*vc.y + + upper.z*vc.z + vc.w ) < 0.0 ) && + (( upper.x*vc.x + lower.y*vc.y + + lower.z*vc.z + vc.w ) < 0.0 ) && + (( upper.x*vc.x + upper.y*vc.y + + lower.z*vc.z + vc.w ) < 0.0 ) && + (( lower.x*vc.x + upper.y*vc.y + + upper.z*vc.z + vc.w ) < 0.0 ) && + (( lower.x*vc.x + lower.y*vc.y + + upper.z*vc.z + vc.w ) < 0.0 ) && + (( lower.x*vc.x + lower.y*vc.y + + lower.z*vc.z + vc.w ) < 0.0 ) && + (( lower.x*vc.x + upper.y*vc.y + + lower.z*vc.z + vc.w ) < 0.0 )) { + // all corners outside this frustum plane + // System.err.println("*** all corners outside this frustum plane"); + return false; + } + } + + return true; + } + + /** + * Returns a string representation of this class. + */ + @Override + public String toString() { + return new String( "Bounding box: Lower="+lower.x+" "+ + lower.y+" "+lower.z+" Upper="+upper.x+" "+ + upper.y+" "+upper.z ); + } + +private void setEmptyBounds() { + lower.set( 1.0d, 1.0d, 1.0d); + upper.set(-1.0d, -1.0d, -1.0d); + boundsIsInfinite = false; + boundsIsEmpty = true; +} + +private void setInfiniteBounds() { + lower.set(Double.NEGATIVE_INFINITY, + Double.NEGATIVE_INFINITY, + Double.NEGATIVE_INFINITY); + upper.set(Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY); + + boundsIsInfinite = true; + boundsIsEmpty = false; +} + + 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 (Double.isNaN(lower.x + lower.y + lower.z + upper.x + upper.y + upper.z)) { + 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 ? +@Override +Point3d getCenter() { + Point3d cent = new Point3d(); + cent.add(upper, lower); + cent.scale(0.5d); + return cent; +} + +@Override +public void getCenter(Point3d center) { + center.add(lower, upper); + center.scale(0.5d); +} + + void translate(BoundingBox bbox, Vector3d value) { + if (bbox == null || bbox.boundsIsEmpty) { + setEmptyBounds(); + return; + } + + if (bbox.boundsIsInfinite) { + setInfiniteBounds(); + 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 + */ + @Override + 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(); + } + } + + @Override + int getPickType() { + return PickShape.PICKBOUNDINGBOX; + } +} + diff --git a/src/javax/media/j3d/BoundingLeaf.java b/src/javax/media/j3d/BoundingLeaf.java new file mode 100644 index 0000000..3037abe --- /dev/null +++ b/src/javax/media/j3d/BoundingLeaf.java @@ -0,0 +1,186 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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. + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_REGION_READ + }; + + /** + * Constructs a BoundingLeaf node with a null (empty) bounding region. + */ + public BoundingLeaf() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + void createRetained() { + this.retained = new BoundingLeafRetained(); + this.retained.setSource(this); + } + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + BoundingLeaf bl = new BoundingLeaf(); + bl.duplicateNode(this, forceDuplicate); + return bl; + } + + + + /** + * Copies all BoundingLeaf information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + super.duplicateAttributes(originalNode, forceDuplicate); + + ((BoundingLeafRetained) retained).initRegion( + ((BoundingLeafRetained) originalNode.retained).getRegion()); + } + +} diff --git a/src/javax/media/j3d/BoundingLeafRetained.java b/src/javax/media/j3d/BoundingLeafRetained.java new file mode 100644 index 0000000..309922f --- /dev/null +++ b/src/javax/media/j3d/BoundingLeafRetained.java @@ -0,0 +1,285 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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 = new J3dMessage(); + 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; + } + + + @Override + 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 = 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); + } + } + + @Override + 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; + } + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + region.transform(xform.transform); + } + +} diff --git a/src/javax/media/j3d/BoundingPolytope.java b/src/javax/media/j3d/BoundingPolytope.java new file mode 100644 index 0000000..37d47c0 --- /dev/null +++ b/src/javax/media/j3d/BoundingPolytope.java @@ -0,0 +1,1783 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector4d; + +/** + * 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. + *

+ * Each plane in the BoundingPolytope specifies a half-space defined + * by the equation: + *

    + * Ax + By + Cz + D <= 0 + *
+ * 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[0] : x <= 1 (1,0,0,-1)
+ * planes[1] : -x <= 1 (-1,0,0,-1)
+ * planes[2] : y <= 1 (0,1,0,-1)
+ * planes[3] : -y <= 1 (0,-1,0,-1)
+ * planes[4] : z <= 1 (0,0,1,-1)
+ * planes[5] : -z <= 1 (0,0,-1,-1)
+ * + */ + 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(); // XXXX: 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(); // XXXX: 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(); // XXXX: 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(); // XXXX: 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= boundsObjects.length ) { // all bounds objects were empty + boundsIsEmpty = true; + boundsIsInfinite = false; + initEmptyPolytope(); + computeAllVerts(); // XXXX: 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(); // XXXX: 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(); // XXXX: 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 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(); // XXXX: 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;kbounds object is + * equal to this BoundingPolytope object. They are equal if the + * specified bounds object is an instance of + * BoundingPolytope and all of the data + * members of bounds 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 bounds; + * otherwise false + * + * @since Java 3D 1.2 + */ + @Override + 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 + */ + @Override + public int hashCode() { + long bits = 1L; + + for (int i = 0; i < planes.length; i++) { + bits = J3dHash.mixDoubleBits(bits, planes[i].x); + bits = J3dHash.mixDoubleBits(bits, planes[i].y); + bits = J3dHash.mixDoubleBits(bits, planes[i].z); + bits = J3dHash.mixDoubleBits(bits, planes[i].w); + } + + return J3dHash.finish(bits); + } + + + /** + * 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 + */ + @Override + 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 + */ + @Override + 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) + return; // no non empty bounds so do not modify current bounds + + if(boundsIsEmpty) + this.set(boundsObjects[i++]); + + if(boundsIsInfinite) + return; + + for(;i 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 + */ + @Override + 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 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 + */ + @Override + 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 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 + */ + @Override + 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(boundsObject); + sphere.transform(matrix); + this.set(sphere); + } else if( boundsObject.boundId == BOUNDING_BOX){ + BoundingBox box = new BoundingBox(boundsObject); + box.transform(matrix); + this.set(box); + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope polytope = new 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 + */ + @Override + public void transform( Transform3D matrix) { + + if(boundsIsInfinite) + return; + + int i; + double invMag; + Transform3D invTrans = new Transform3D(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] ); + } + + for(i=0;i= 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 + */ + @Override + boolean intersect(Point3d origin, Vector3d direction, Point4d position ) { + double t,v0,vd,x,y,z,invMag; + double dx, dy, dz; + int i; + + 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 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 + */ + @Override + boolean intersect( Point3d start, Point3d end, Point4d position ) { + double t,v0,vd,x,y,z; + int i; + + //System.err.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= 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.err.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 + */ + @Override + 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; + + if( boundsIsEmpty ) { + return false; + } + + if( boundsIsInfinite ) { + return true; + } + + for(i=0;i= 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.err.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 not empty. + * @return true if the bounding polytope is empty; + * otherwise, it returns false + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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= 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 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= verts.length) { + Point3d newVerts[] = new Point3d[nVerts << 1]; + for(int i=0;i 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(); + } + + @Override + Point3d getCenter() { + return centroid; + } + +@Override +public void getCenter(Point3d center) { + center.set(centroid); +} + + /** + * if the passed the "region" is same type as this object + * then do a copy, otherwise clone the Bounds and + * return + */ + @Override + 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 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) { + boundId = BOUNDING_SPHERE; + center = new Point3d(); + + if (boundsObjects == null || boundsObjects.length <= 0) { + setEmptyBounds(); + return; + } + + // find first non empty bounds object + int i = 0; + while( boundsObjects[i] == null && i < boundsObjects.length) { + i++; + } + + if (i >= boundsObjects.length) { // all bounds objects were empty + setEmptyBounds(); + return; + } + + this.set(boundsObjects[i++]); + if(boundsIsInfinite) + return; + + Point3d[] boxVerts = null; + for(;i 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 + */ +@Override +public void getCenter(Point3d center) { + center.set(this.center); +} + +/** + * 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.set(center); + updateBoundsStates(); +} + + /** + * Sets the value of this BoundingSphere. + * @param boundsObject another bounds object + */ + @Override + public void set(Bounds boundsObject){ + int i; + + if (boundsObject == null || boundsObject.boundsIsEmpty) { + setEmptyBounds(); + return; + } + + if( boundsObject.boundsIsInfinite ) { + setInfiniteBounds(); + return; + } + + 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 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 + */ + @Override + public Object clone() { + return new BoundingSphere(this.center, this.radius); + } + + + /** + * Indicates whether the specified bounds object is + * equal to this BoundingSphere object. They are equal if the + * specified bounds object is an instance of + * BoundingSphere and all of the data + * members of bounds 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 bounds; + * otherwise false + * + * @since Java 3D 1.2 + */ + @Override + 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 + */ + @Override + public int hashCode() { + long bits = 1L; + bits = J3dHash.mixDoubleBits(bits, radius); + bits = J3dHash.mixDoubleBits(bits, center.x); + bits = J3dHash.mixDoubleBits(bits, center.y); + bits = J3dHash.mixDoubleBits(bits, center.z); + return J3dHash.finish(bits); + } + + + /** + * 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 + */ + @Override + 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 + */ + @Override + 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) + return; // no non empty bounds so do not modify current bounds + + if( boundsIsEmpty) + this.set(boundsObjects[i++]); + + if(boundsIsInfinite) + return; + + for(;i 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 + */ + @Override + public void combine(Point3d point) { + double 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 + */ + @Override + 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 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 + */ + @Override + public void transform( Bounds boundsObject, Transform3D matrix) { + if (boundsObject == null || boundsObject.boundsIsEmpty) { + setEmptyBounds(); + return; + } + + if (boundsObject.boundsIsInfinite) { + setInfiniteBounds(); + return; + } + + if (boundsObject.boundId == BOUNDING_BOX) { + BoundingBox tmpBox = new BoundingBox(boundsObject); + tmpBox.transform(matrix); + this.set(tmpBox); + } + else if (boundsObject.boundId == BOUNDING_SPHERE) { + this.set(boundsObject); + this.transform(matrix); + } + else if (boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope tmpPolytope = new BoundingPolytope(boundsObject); + tmpPolytope.transform(matrix); + this.set(tmpPolytope); + } else { + throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere5")); + } + } + + /** + * Transforms this bounding sphere by the given matrix. + */ + @Override + public void transform( Transform3D trans) { + double scale; + + if(boundsIsInfinite) + return; + + trans.transform(center); + scale = trans.getDistanceScale(); + radius = radius * scale; + if (Double.isNaN(radius)) { + setEmptyBounds(); + 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 + */ + @Override + 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,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.err.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.err.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.err.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.err.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 + */ + @Override + 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 + */ + @Override + 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,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.err.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.err.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 + */ + @Override + 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.err.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.err.println("ray points away from sphere" ); + return false; // ray points away from sphere + } + + t2hc = rad2 - l2oc + tca*tca; + + if( t2hc > 0.0 ){ + // System.err.println("ray hits sphere" ); + return true; // ray hits sphere + }else { + // System.err.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; // XXXX: 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.err.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.err.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.err.println("ray hits sphere" ); + return true; // ray hits sphere + }else { + // System.err.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 + */ + @Override + 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 not empty. + * @return true if the bounding sphere is empty; + * otherwise, it returns false + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + public boolean intersect(Bounds boundsObject) { + double distsq, radsq; + BoundingSphere sphere; + + 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 + */ + @Override + 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) { + newBoundSphere.set(null); + return false; + } + + if (boundsIsInfinite && !boundsObject.boundsIsInfinite) { + newBoundSphere.set(boundsObject); + return true; + } + + if (boundsObject.boundsIsInfinite) { + newBoundSphere.set(this); + return true; + } + + 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 { + newBoundSphere.set(null); + return false; + } + } else if( boundsObject.boundId == BOUNDING_SPHERE ) { + BoundingSphere sphere = (BoundingSphere)boundsObject; + 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) { + newBoundSphere.set(null); + return 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; + } 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; + } 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; + } + + newBoundSphere.updateBoundsStates(); + return true; + + } 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 { + newBoundSphere.set(null); + 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) { + newBoundSphere.set(null); + 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 + newBoundSphere.set(null); + return false; + } + + boolean status = false; + double newRadius; + Point3d newCenter = new Point3d(); + BoundingBox tbox = new BoundingBox(); + + for(i=0;i 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) + newBoundSphere.set(null); + return status; + } + + /** + * Finds closest bounding object that intersects this bounding sphere. + * @param boundsObjects an array of bounds objects + * @return closest bounding object + */ + @Override + 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 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.err.println("Tossing " + i + " " + dist + " " + radius); + return(false); + } + } + return true; + } + + /** + * Returns a string representation of this class. + */ + @Override + public String toString() { + return new String( "Center="+center+" Radius="+radius); + } + +private void setEmptyBounds() { + center.set(0.0d, 0.0d, 0.0d); + radius = -1.0; + boundsIsInfinite = false; + boundsIsEmpty = true; +} + +private void setInfiniteBounds() { + center.set(0.0d, 0.0d, 0.0d); + radius = Double.POSITIVE_INFINITY; + boundsIsEmpty = false; + boundsIsInfinite = true; +} + + private void updateBoundsStates() { + + if (Double.isNaN(radius + center.x + center.y + center.z)) { + 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; + } + } + } + + @Override + 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 + */ + @Override + 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(); + } + } + + @Override + int getPickType() { + return PickShape.PICKBOUNDINGSPHERE; + } +} + + + + diff --git a/src/javax/media/j3d/Bounds.java b/src/javax/media/j3d/Bounds.java new file mode 100644 index 0000000..9672859 --- /dev/null +++ b/src/javax/media/j3d/Bounds.java @@ -0,0 +1,673 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Matrix3d; +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector4d; + +/** + * 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. + */ + @Override + public abstract Object clone(); + + + /** + * Indicates whether the specified bounds object is + * equal to this Bounds object. They are equal if both the + * specified bounds object and this Bounds are + * instances of the same Bounds subclass and all of the data + * members of bounds 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 bounds; + * otherwise false + * + * @since Java 3D 1.2 + */ + @Override + 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 + */ + @Override + 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(); + +/** + * Gets the centroid of this bounding region. + * @param center a Point to receive the centroid of the bounding region + */ +public abstract void getCenter(Point3d center); + + /** + * 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 not 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.err.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 + + + /* + * 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.err.println("closest_point():\nincoming g="+" "+g.x+" "+g.y+ + " "+g.z); + } + + converged = false; + firstPoint = true; + firstInside = false; + + Vector4d pln; + + while( !converged ) { + if (debug) { + System.err.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.err.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.err.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.err.println(" hessin= "); + System.err.println(h11+" "+h12+" "+h13); + System.err.println(" "+h22+" "+h23); + System.err.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.err.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.err.println(" l12,l13,l23 "+l12+" "+l13+" "+l23); + System.err.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.err.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.err.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.err.println("p->n direction: " + delta); + Matrix3d hMatrix = new Matrix3d(); + // 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.err.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.err.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.err.println("returning pnt ("+new_point.x+" "+ + new_point.y+" "+new_point.z+")"); + + if(firstInside) System.err.println("input point inside polytope "); + } + return firstInside; + } + + boolean intersect_ptope_sphere( BoundingPolytope polyTope, + BoundingSphere sphere) { + Point3d p = new Point3d(); + boolean inside; + + + if (debug) { + System.err.println("ptope_sphere intersect sphere ="+sphere); + } + inside = closest_point( sphere.center, polyTope.planes, p ); + if (debug) { + System.err.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.err.println("ptope_sphere returns false"); + } + return false; + } else { + if (debug) { + System.err.println("ptope_sphere returns true"); + } + return true; + } + } else { + if (debug) { + System.err.println("ptope_sphere returns true"); + } + return true; + } + } + + boolean intersect_ptope_abox( BoundingPolytope polyTope, BoundingBox box) { + Vector4d planes[] = new Vector4d[6]; + + if (debug) { + System.err.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.err.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.err.println("ptope_ptope: first g = "+g); + } + intersect = closest_point( g, poly2.planes, p); + + if (intersect) { + return true; + } + + if (debug) { + System.err.println("first p = "+p+"\n"); + } + intersect = closest_point( p, poly1.planes, gnew); + if (debug) { + System.err.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.err.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.err.println("gnew = "+gnew+" intersect="+ + intersect); + } + } + } else { + p.set(pnew); + break; + } + prevDist = dist; + } + + if (debug) { + System.err.println("gnew="+" "+gnew.x+" "+gnew.y+" "+gnew.z); + System.err.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/javax/media/j3d/BranchGroup.java b/src/javax/media/j3d/BranchGroup.java new file mode 100644 index 0000000..41fc93a --- /dev/null +++ b/src/javax/media/j3d/BranchGroup.java @@ -0,0 +1,580 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + *

    + *
  • 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).
  • + *

    + *

  • A BranchGroup may be inserted into a virtual universe by attaching it to a + * Locale. The entire subgraph is then said to be live.
  • + *

    + *

  • A BranchGroup that is contained within another subgraph may be reparented or + * detached at run time if the appropriate capabilities are set.
  • + *
+ * 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. + */ + @Override + 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(); + } + + + void validateModeFlagAndPickShape(int mode, int flags, PickShape pickShape) { + + if(isLive()==false) { + throw new IllegalStateException(J3dI18N.getString("BranchGroup3")); + } + + if((mode != PickInfo.PICK_BOUNDS) && (mode != PickInfo.PICK_GEOMETRY)) { + + throw new IllegalArgumentException(J3dI18N.getString("BranchGroup4")); + } + + if((pickShape instanceof PickPoint) && (mode == PickInfo.PICK_GEOMETRY)) { + throw new IllegalArgumentException(J3dI18N.getString("BranchGroup5")); + } + + if(((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) && + ((flags & PickInfo.ALL_GEOM_INFO) != 0)) { + throw new IllegalArgumentException(J3dI18N.getString("BranchGroup6")); + } + + if((mode == PickInfo.PICK_BOUNDS) && + (((flags & (PickInfo.CLOSEST_GEOM_INFO | + PickInfo.ALL_GEOM_INFO | + PickInfo.CLOSEST_DISTANCE | + PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) { + + throw new IllegalArgumentException(J3dI18N.getString("BranchGroup7")); + } + + if((pickShape instanceof PickBounds) && + (((flags & (PickInfo.CLOSEST_GEOM_INFO | + PickInfo.ALL_GEOM_INFO | + PickInfo.CLOSEST_DISTANCE | + PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) { + + throw new IllegalArgumentException(J3dI18N.getString("BranchGroup8")); + } + + } + + /** + * Returns an array referencing all the items that are pickable below this + * BranchGroup 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 ((BranchGroupRetained)this.retained).pickAll(pickShape); + + } + + /** + * Returns an array unsorted references to all the PickInfo objects that are + * pickable below this BranchGroup that intersect with PickShape. + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if BranchGroup is not live. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see Locale#pickAll(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + + public PickInfo[] pickAll( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + return ((BranchGroupRetained)this.retained).pickAll(mode, flags, pickShape); + + } + + + /** + * Returns a sorted array of references to all the Pickable items that + * intersect with the pickShape. Element [0] references the item closest + * to origin of PickShape successive array elements are further + * from the origin + * + * 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 ((BranchGroupRetained)this.retained).pickAllSorted(pickShape); + + } + + + /** + * Returns a sorted array of PickInfo references to all the pickable + * items that intersect with the pickShape. Element [0] references + * the item closest to origin of PickShape successive array + * elements are further from the origin + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if BranchGroup is not live. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see Locale#pickAllSorted(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo[] pickAllSorted( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + return ((BranchGroupRetained)this.retained).pickAllSorted(mode, flags, pickShape); + + } + + /** + * Returns a SceneGraphPath that references the pickable item + * closest to the origin of pickShape. + * + * 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 ((BranchGroupRetained)this.retained).pickClosest(pickShape); + + } + + /** + * Returns a PickInfo which references the pickable item + * which is closest to the origin of pickShape. + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if BranchGroup is not live. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see Locale#pickClosest(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo pickClosest( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + return ((BranchGroupRetained)this.retained).pickClosest(mode, flags, pickShape); + + } + + + /** + * Returns a reference to any item that is Pickable below this BranchGroup that + * intersects with pickShape. + * @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 ((BranchGroupRetained)this.retained).pickAny(pickShape); + + } + + /** + * Returns a PickInfo which references the pickable item below this + * BranchGroup that intersects with pickShape. + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if BranchGroup is not live. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see Locale#pickAny(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo pickAny( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + return ((BranchGroupRetained)this.retained).pickAny(mode, flags, pickShape); + + } + + /** + * Creates a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + + BranchGroup bg = new BranchGroup(); + bg.duplicateNode(this, forceDuplicate); + return bg; + + } + +} diff --git a/src/javax/media/j3d/BranchGroupRetained.java b/src/javax/media/j3d/BranchGroupRetained.java new file mode 100644 index 0000000..47372d0 --- /dev/null +++ b/src/javax/media/j3d/BranchGroupRetained.java @@ -0,0 +1,377 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +/** + * 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 + */ + @Override + Locale getLocale() { + return locale; + } + + /** + * Detaches this BranchGroup from its parent. + */ + void detach() { + dirtyBoundsCache(); + if (universe != null) { + universe.resetWaitMCFlag(); + synchronized (universe.sceneGraphLock) { + boolean isLive = source.isLive(); + if (isLive) { + notifySceneGraphChanged(true); + } + GroupRetained oldParent = (GroupRetained)parent; + do_detach(); + universe.setLiveState.clear(); + if (isLive) { + if (oldParent==null) { + universe.notifyStructureChangeListeners(false,locale,(BranchGroup)this.source); + } else { + universe.notifyStructureChangeListeners(false,oldParent.source, (BranchGroup)this.source); + } + } + } + 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); + } + } + + @Override + 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= 0) { + setAuxData(s, j, hkIndex); + + } else { + // XXXX: change this to an assertion exception + MasterControl.getCoreLogger().severe("Can't Find matching hashKey in setNodeData."); + } + } + } + } + + + @Override + void setAuxData(SetLiveState s, int index, int hkIndex) { + super.setAuxData(s, index, hkIndex); + + BranchGroupRetained path[] = 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. + */ + @Override + 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); + } + + @Override + 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.err.println(); + } + } + } + + @Override + 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; + } + + SceneGraphPath[] pickAll(PickShape pickShape) { + + PickInfo[] pickInfoArr = pickAll(PickInfo.PICK_BOUNDS, + PickInfo.SCENEGRAPHPATH, pickShape); + + if(pickInfoArr == null) { + return null; + } + + SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length]; + for( int i=0; i 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; + + } + + private void computeVertex( int a, int b, int c, Point3d vert) { + double det; + + 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 ){ + // System.err.println("************** Two planes are parallel : det = " + det); + 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; + + } + +} diff --git a/src/javax/media/j3d/CachedTargets.java b/src/javax/media/j3d/CachedTargets.java new file mode 100644 index 0000000..0723d4c --- /dev/null +++ b/src/javax/media/j3d/CachedTargets.java @@ -0,0 +1,122 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + * 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. + *

+ * 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. + *

+ * On-screen Rendering vs. Off-screen Rendering + *

+ * 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. + *

+ * Off-screen Canvas3Ds must not be added to any Container. Java 3D + * renders to off-screen canvases in response to the + * renderOffScreenBuffer 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. + *

+ * 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. + *

+ * The getOffScreenBuffer method retrieves the off-screen + * buffer for this Canvas3D. + *

+ * 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. + *

+ * 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). + *

+ * Accessing and Modifying an Eye's Image Plate Position + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * Monoscopic View Policy + *

+ * 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. + *

+ * Immediate Mode Rendering + *

+ * 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. + *

+ * 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. + *

+ * 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 stopRenderer method, prior to adding the + * Canvas3D object to an active View object (that is, one that is + * attached to a live ViewPlatform object). + *

+ * Other Canvas3D methods related to immediate mode rendering are: + *

+ *

    + * getGraphicsContext3D retrieves the immediate-mode + * 3D graphics context associated with this Canvas3D. It creates a + * new graphics context if one does not already exist. + *

    + * getGraphics2D retrieves the + * 2D graphics object associated with this Canvas3D. It creates a + * new 2D graphics object if one does not already exist. + *

    + * swap 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. + *

+ * + *

+ * Mixed Mode Rendering + *

+ * 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. + * + *

+ * Canvas3D methods related to mixed mode rendering are: + * + *

+ *

    + * preRender called by the Java 3D rendering loop after + * clearing the canvas and before any rendering has been done for + * this frame. + *

    + * postRender called by the Java 3D rendering loop after + * completing all rendering to the canvas for this frame and before + * the buffer swap. + *

    + * postSwap 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. + *

    + * renderField 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. + *

+ *

+ * The above callback methods are called by the Java 3D rendering system + * and should not be called by an application directly. + * + *

+ * The basic Java 3D stereo rendering loop, + * executed for each Canvas3D, is as follows: + *

    + * 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
    + * 
+ *

+ * The basic Java 3D monoscopic rendering loop is as follows: + *

    + * 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
    + * 
+ *

+ * 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. + * + *

+ * Serialization + *

+ * Canvas3D does not support serialization. An attempt to + * serialize a Canvas3D object will result in an + * UnsupportedOperationException being thrown. + * + *

+ * Additional Information + *

+ * For more information, see the + * Introduction to the Java 3D API and + * View Model + * documents. + * + * @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 VIEW_MATRIX_DIRTY = 0x8000; + // static final int SHADER_DIRTY = 0x10000; Not ready for this yet -- JADA + + // + // Flag that indicates whether this Canvas3D is an off-screen Canvas3D + // + boolean offScreen = false; + + // + // Issue 131: Flag that indicates whether this Canvas3D is a manually + // rendered Canvas3D (versus an automatically rendered Canvas3D). + // + // NOTE: manualRendering only applies to off-screen Canvas3Ds at this time. + // We have no plans to ever change this, but if we do, it might be necessary + // to determine which, if any, of the uses of "manualRendering" should be + // changed to "manualRendering&&offScreen" + // + boolean manualRendering = false; + + // user specified offScreen Canvas location + Point offScreenCanvasLoc; + + // user specified offScreen Canvas dimension + Dimension offScreenCanvasSize; + + // + // Flag that indicates whether off-screen rendering is in progress or not + // + volatile boolean offScreenRendering = false; + + // + // Flag that indicates we are waiting for an off-screen buffer to be + // created or destroyed by the Renderer. + // + volatile boolean offScreenBufferPending = false; + + // + // ImageComponent used for off-screen rendering + // + ImageComponent2D offScreenBuffer = null; + + // 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; + + // User requested stencil size + int requestedStencilSize; + + // Actual stencil size return for this canvas + int actualStencilSize; + + // True if stencil buffer is available for user + boolean userStencilAvailable; + + // True if stencil buffer is available for system ( decal ) + boolean systemStencilAvailable; + + // + // 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 + private 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; + + // number of active/enabled texture unit + int numActiveTexUnit = 0; + + // index iof last enabled texture unit + int lastActiveTexUnit = -1; + + // True if shadingLanguage is supported, otherwise false. + boolean shadingLanguageGLSL = false; + + // Query properties + J3dQueryProps queryProps; + + // Flag indicating a fatal rendering error of some sort + private boolean fatalError = false; + + // + // 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; + + // Issue 109: View cache for this canvas, for computing view frustum planes + CanvasViewCache canvasViewCacheFrustum = 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); + + // Issue 163: Array of dirty bits is used because the Renderer and + // RenderBin run asynchronously. Now that they each have a separate + // instance of CanvasViewCache (due to the fix for Issue 109), they + // need separate dirty bits. Array element 0 is used for the Renderer and + // element 1 is used for the RenderBin. + static final int RENDERER_DIRTY_IDX = 0; + static final int RENDER_BIN_DIRTY_IDX = 1; + int[] cvDirtyMask = new int[2]; + + // 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 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; + + // Opaque object representing the underlying drawable (window). This + // is defined by the Pipeline. + Drawable drawable = null; + + // graphicsConfigTable is a static hashtable which allows getBestConfiguration() + // in NativeConfigTemplate3D to map a GraphicsConfiguration to the pointer + // to the actual GLXFBConfig that glXChooseFBConfig() 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 graphicsConfigTable = + new Hashtable(); + + // The native graphics version, vendor, and renderer information + String nativeGraphicsVersion = ""; + String nativeGraphicsVendor = ""; + String nativeGraphicsRenderer = ""; + + boolean firstPaintCalled = false; + + // This reflects whether or not this canvas has seen an addNotify. It is + // forced to true for off-screen canvases + boolean added = false; + + // Flag indicating whether addNotify has been called (so we don't process it twice). + private boolean addNotifyCalled = false; + + // This is the id for the underlying graphics context structure. + Context ctx = null; + + // 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; + ShaderBin shaderBin = 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; + Transform3D projTrans = 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; + + /** + * 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; + + ShaderProgramRetained shaderProgram = null; + + // only used in Mixed Mode rendering + Object appHandle = null; + + /** + * 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; + // an unique number to identify this canvas : ( canvasBit = 1 << canvasId) + int canvasId = 0; + // Indicates whether the canvasId has been allocated + private boolean canvasIdAlloc = false; + + // Avoid using this as lock, it cause deadlock + Object cvLock = new Object(); + Object evaluateLock = new Object(); + Object dirtyMaskLock = new Object(); + + // For D3D, instead of using the same variable in Renderer, + // each canvas has to build its own displayList. + boolean needToRebuildDisplayList = false; + + // 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; + static final int TEXTURE_NON_POWER_OF_TWO = 0x8000; + static final int TEXTURE_AUTO_MIPMAP_GENERATION = 0x10000; + + int textureExtendedFeatures = 0; + + // Extensions supported by the underlying canvas + // + // NOTE: we should remove EXT_BGR and EXT_ABGR when the imaging code is + // rewritten + // killed global alpha + //static final int SUN_GLOBAL_ALPHA = 0x1; + static final int EXT_ABGR = 0x2; + static final int EXT_BGR = 0x4; + static final int MULTISAMPLE = 0x8; + + // The following 10 variables are set by the native + // createNewContext()/createQueryContext() methods + + // Supported Extensions + int extensionsSupported = 0; + + // Anisotropic Filter degree + float anisotropicDegreeMax = 1.0f; + + // Texture Boundary Width Max + int textureBoundaryWidthMax = 0; + + boolean multiTexAccelerated = false; + + // Max number of texture coordinate sets + int maxTexCoordSets = 1; + + // Max number of fixed-function texture units + int maxTextureUnits = 1; + + // Max number of fragment shader texture units + int maxTextureImageUnits = 0; + + // Max number of vertex shader texture units + int maxVertexTextureImageUnits = 0; + + // Max number of combined shader texture units + int maxCombinedTextureImageUnits = 0; + + // Max number of vertex attrs (not counting coord, etc.) + int maxVertexAttrs = 0; + + // End of variables set by createNewContext()/createQueryContext() + + // The total available number of texture units used by either the + // fixed-function or programmable shader pipeline. + // This is computed as: max(maxTextureUnits, maxTextureImageUnits) + int maxAvailableTextureUnits; + + // Texture Width, Height Max + int textureWidthMax = 0; + int textureHeightMax = 0; + + // Texture3D Width, Heigh, Depth Max + int texture3DWidthMax = -1; + int texture3DHeightMax = -1; + int texture3DDepthMax = -1; + + // 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; + static final int SHADERBIN_BIT = 0x6; + + // 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[7]; + + /** + * 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. + private CanvasViewEventCatcher canvasViewEventCatcher; + + // The top-level parent window for this canvas. + private Window windowParent; + + // Issue 458 - list of all parent containers for this canvas + // (includes top-level parent window) + private LinkedList containerParentList = new LinkedList(); + + // 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; + + // 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; + } + + // Returns true if this is a valid graphics configuration, obtained + // via a GraphicsConfigTemplate3D. + private static boolean isValidConfig(GraphicsConfiguration gconfig) { + // If this is a valid GraphicsConfiguration object, then it will + // be in the graphicsConfigTable + return graphicsConfigTable.containsKey(gconfig); + } + + // Checks the given graphics configuration, and throws an exception if + // the config is null or invalid. + private static synchronized GraphicsConfiguration + checkForValidGraphicsConfig(GraphicsConfiguration gconfig, boolean offScreen) { + + // Issue 266 - for backwards compatibility with legacy applications, + // we will accept a null GraphicsConfiguration for an on-screen Canvas3D + // only if the "allowNullGraphicsConfig" system property is set to true. + if (!offScreen && VirtualUniverse.mc.allowNullGraphicsConfig) { + if (gconfig == null) { + // Print out warning if Canvas3D is called with a + // null GraphicsConfiguration + System.err.println(J3dI18N.getString("Canvas3D7")); + System.err.println(" " + J3dI18N.getString("Canvas3D18")); + + // Use a default graphics config + gconfig = defaultGraphicsConfiguration(); + } + } + + // Validate input graphics config + if (gconfig == null) { + throw new NullPointerException(J3dI18N.getString("Canvas3D19")); + } else if (!isValidConfig(gconfig)) { + throw new IllegalArgumentException(J3dI18N.getString("Canvas3D17")); + } + + return gconfig; + } + + // Return the actual graphics config that will be used to construct + // the AWT Canvas. This is permitted to be non-unique or null. + private static GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig) { + return Pipeline.getPipeline().getGraphicsConfig(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: + *

    + * left manual eye in image plate : (0.142, 0.135, 0.4572)
    + * right manual eye in image plate : (0.208, 0.135, 0.4572)
    + * stereo enable : true
    + * double buffer enable : true
    + * monoscopic view policy : View.CYCLOPEAN_EYE_VIEW
    + * off-screen mode : false
    + * off-screen buffer : null
    + * off-screen location : (0,0)
    + *
+ * + * @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(null, checkForValidGraphicsConfig(graphicsConfiguration, false), 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) { + this(null, checkForValidGraphicsConfig(graphicsConfiguration, offScreen), offScreen); + } + + // Private constructor only called by the two public constructors after + // they have validated the graphics config (and possibly constructed a new + // default config). + // The graphics config must be valid, unique, and non-null. + private Canvas3D(Object dummyObj1, + GraphicsConfiguration graphicsConfiguration, + boolean offScreen) { + this(dummyObj1, + graphicsConfiguration, + getGraphicsConfig(graphicsConfiguration), + offScreen); + } + + // Private constructor only called by the previous private constructor. + // The graphicsConfiguration parameter is used by Canvas3D to lookup the + // graphics device and graphics template. The graphicsConfiguration2 + // parameter is generated by the Pipeline from graphicsConfiguration and + // is only used to initialize the java.awt.Canvas. + private Canvas3D(Object dummyObj1, + GraphicsConfiguration graphicsConfiguration, + GraphicsConfiguration graphicsConfiguration2, + boolean offScreen) { + + super(graphicsConfiguration2); + + this.offScreen = offScreen; + this.graphicsConfiguration = graphicsConfiguration; + + // Issue 131: Set the autoOffScreen variable based on whether this + // canvas3d implements the AutoOffScreenCanvas3D tagging interface. + // Eventually, we may replace this with an actual API. + boolean autoOffScreenCanvas3D = false; + if (this instanceof AutoOffScreenCanvas3D) { + autoOffScreenCanvas3D = true; + } + + // Throw an illegal argument exception if an on-screen canvas is tagged + // as an auto-off-screen canvas + if (autoOffScreenCanvas3D && !offScreen) { + throw new IllegalArgumentException(J3dI18N.getString("Canvas3D25")); + } + + // Issue 163 : Set dirty bits for both Renderer and RenderBin + cvDirtyMask[0] = VIEW_INFO_DIRTY; + cvDirtyMask[1] = VIEW_INFO_DIRTY; + + GraphicsConfigInfo gcInfo = graphicsConfigTable.get(graphicsConfiguration); + requestedStencilSize = gcInfo.getGraphicsConfigTemplate3D().getStencilSize(); + + if (offScreen) { + + // Issue 131: set manual rendering flag based on whether this is + // an auto-off-screen Canvas3D. + manualRendering = !autoOffScreenCanvas3D; + + screen = new Screen3D(graphicsConfiguration, offScreen); + + // QUESTION: keep a list of off-screen Screen3D objects? + // Does this list need to be grouped by GraphicsDevice? + + synchronized(dirtyMaskLock) { + cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY; + cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY; + } + + // this canvas will not receive the paint callback, + // so we need to set the necessary flags here + firstPaintCalled = true; + + if (manualRendering) { + // since this canvas will not receive the addNotify + // callback from AWT, set the added flag here for + // evaluateActive to work correctly + added = true; + } + + evaluateActive(); + + // create the rendererStructure object + //rendererStructure = new RendererStructure(); + offScreenCanvasLoc = new Point(0, 0); + offScreenCanvasSize = new Dimension(0, 0); + + this.setLocation(offScreenCanvasLoc); + this.setSize(offScreenCanvasSize); + newSize = offScreenCanvasSize; + newPosition = offScreenCanvasLoc; + + // Issue 131: create event catchers for auto-offScreen + if (!manualRendering) { + eventCatcher = new EventCatcher(this); + canvasViewEventCatcher = new CanvasViewEventCatcher(this); + } + } else { + + GraphicsDevice graphicsDevice; + graphicsDevice = graphicsConfiguration.getDevice(); + + eventCatcher = new EventCatcher(this); + canvasViewEventCatcher = new CanvasViewEventCatcher(this); + + synchronized(VirtualUniverse.mc.deviceScreenMap) { + screen = VirtualUniverse.mc.deviceScreenMap.get(graphicsDevice); + + if (screen == null) { + screen = new Screen3D(graphicsConfiguration, offScreen); + VirtualUniverse.mc.deviceScreenMap.put(graphicsDevice, screen); + } + } + + } + + lights = new LightRetained[VirtualUniverse.mc.maxLights]; + frameCount = new int[VirtualUniverse.mc.maxLights]; + for (int i=0; i + * Updates to live Geometry, Texture, and ImageComponent objects + * in the scene graph are not allowed from this method. + * + *

+ * NOTE: Applications should not 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. + * + *

+ * Updates to live Geometry, Texture, and ImageComponent objects + * in the scene graph are not allowed from this method. + * + *

+ * NOTE: Applications should not 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. + * + *

+ * Updates to live Geometry, Texture, and ImageComponent objects + * in the scene graph are not allowed from this method. + * + *

+ * NOTE: Applications should not 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. + * + *

+ * Updates to live Geometry, Texture, and ImageComponent objects + * in the scene graph are not allowed from this method. + * + *

+ * NOTE: Applications should not call this method. + *

+ * + * @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() { + // Issue 131: renderer can't be stopped only if it is an offscreen, + // manual canvas. Otherwise, it has to be seen as an onscreen canvas. + if (manualRendering) + 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() { + // Issue 260 : ignore attempt to start renderer if fatal error + if (fatalError) { + return; + } + + 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; + } + + // Returns the state of the fatal error flag + boolean isFatalError() { + return fatalError; + } + + // Sets the fatal error flag to true; stop the renderer for this canvas + void setFatalError() { + fatalError = true; + + if (isRunning) { + isRunning = false; + + if (!manualRendering) { + VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this); + } + } + } + + + /** + * Retrieves a flag indicating whether this Canvas3D is an + * off-screen canvas. + * + * @return true if this Canvas3D is an off-screen canvas; + * false 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. + *

+ * 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. + *

+ * + * @param buffer the image component that will be rendered into by + * subsequent calls to renderOffScreenBuffer. The image component must not + * be part of a live scene graph, nor may it subsequently be made part of a + * live scene graph while being used as an off-screen buffer; an + * IllegalSharingException is thrown in such cases. The buffer may be null, + * indicating that the previous off-screen buffer is released without a new + * buffer being set. + * + * @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 part of a live scene graph + * + * @exception IllegalSharingException if the specified ImageComponent2D is + * being used by an immediate mode context, or by another Canvas3D as + * an off-screen buffer. + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is not ImageClass.BUFFERED_IMAGE. + * + * @exception IllegalArgumentException if the specified + * ImageComponent2D is in by-reference mode and its + * RenderedImage is null. + * + * @exception IllegalArgumentException if the ImageComponent2D format + * is not a 3-component format (e.g., FORMAT_RGB) + * or a 4-component format (e.g., FORMAT_RGBA). + * + * @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) { + int width, height; + boolean freeCanvasId = false; + + if (!offScreen) + throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); + + if (offScreenRendering) + throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2")); + + // Check that offScreenBufferPending is not already set + J3dDebug.doAssert(!offScreenBufferPending, "!offScreenBufferPending"); + + if (offScreenBuffer != null && offScreenBuffer != buffer) { + ImageComponent2DRetained i2dRetained = + (ImageComponent2DRetained)offScreenBuffer.retained; + i2dRetained.setUsedByOffScreen(false); + } + + if (buffer != null) { + ImageComponent2DRetained bufferRetained = + (ImageComponent2DRetained)buffer.retained; + + if (bufferRetained.byReference && + !(bufferRetained.getRefImage(0) instanceof BufferedImage)) { + + throw new IllegalArgumentException(J3dI18N.getString("Canvas3D15")); + } + + if (bufferRetained.getNumberOfComponents() < 3 ) { + throw new IllegalArgumentException(J3dI18N.getString("Canvas3D16")); + } + + if (buffer.isLive()) { + throw new IllegalSharingException(J3dI18N.getString("Canvas3D26")); + } + + if (bufferRetained.getInImmCtx()) { + throw new IllegalSharingException(J3dI18N.getString("Canvas3D27")); + } + + if (buffer != offScreenBuffer && bufferRetained.getUsedByOffScreen()) { + throw new IllegalSharingException(J3dI18N.getString("Canvas3D28")); + } + + bufferRetained.setUsedByOffScreen(true); + + width = bufferRetained.width; + height = bufferRetained.height; + + // Issues 347, 348 - assign a canvasId for off-screen Canvas3D + if (manualRendering) { + sendAllocateCanvasId(); + } + } + else { + width = height = 0; + + // Issues 347, 348 - release canvasId for off-screen Canvas3D + if (manualRendering) { + freeCanvasId = true; + } + } + + if ((offScreenCanvasSize.width != width) || + (offScreenCanvasSize.height != height)) { + + if (drawable != null) { + // Fix for Issue 18 and Issue 175 + // Will do destroyOffScreenBuffer in the Renderer thread. + sendDestroyCtxAndOffScreenBuffer(); + drawable = null; + } + // Issue 396. Since context is invalid here, we should set it to null. + ctx = null; + + // set the canvas dimension according to the buffer dimension + offScreenCanvasSize.setSize(width, height); + this.setSize(offScreenCanvasSize); + + if (width > 0 && height > 0) { + sendCreateOffScreenBuffer(); + } + + } + else if (ctx != null) { + removeCtx(); + } + + if (freeCanvasId) { + sendFreeCanvasId(); + } + + offScreenBuffer = buffer; + + synchronized(dirtyMaskLock) { + cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY; + cvDirtyMask[1] |= 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 postSwap method, or call + * waitForOffScreenRendering. + * + * @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")); + + // Issue 131: Cannot manually render to an automatic canvas. + if (!manualRendering) + throw new IllegalStateException(J3dI18N.getString("Canvas3D24")); + + // Issue 260 : Cannot render if we already have a fatal error + if (fatalError) { + throw new IllegalRenderingStateException(J3dI18N.getString("Canvas3D30")); + } + + 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")); + + // Fix to issue 66 + if ((!active) || (pendingView == null)) { + /* No rendering is performed if this Canvas3D object has not been + added to an active View. */ + return; + } + + // Issue 131: moved code that determines off-screen boundary to separate + // method that is called from the renderer + + offScreenRendering = true; + + // Fix to issue 66. + /* This is an attempt to do the following check in one atomic operation : + ((view != null) && (view.inCanvasCallback)) */ + + boolean inCanvasCallback = false; + try { + inCanvasCallback = view.inCanvasCallback; + + } catch (NullPointerException npe) { + /* Do nothing here */ + } + + if (inCanvasCallback) { + // Here we assume that view is stable if inCanvasCallback + // is true. This assumption is valid among all j3d threads as + // all access to view is synchronized by MasterControl. + // Issue : user threads access to view isn't synchronize hence + // is model will break. + 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 = Screen3D.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 = new J3dMessage(); + 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]; + screen.renderer.args[0] = new Integer(Renderer.REQUESTRENDER); + screen.renderer.args[1] = this; + screen.renderer.args[2] = view; + // This extra argument 3 is needed in MasterControl to + // test whether offscreen Rendering is used or not + screen.renderer.args[3] = null; + + // call renderer doWork directly since we are already in + // the renderer thread + screen.renderer.doWork(0); + } else { + + // XXXX: + // Now we are in trouble, this will cause deadlock if + // waitForOffScreenRendering() is invoked + J3dMessage createMessage = new J3dMessage(); + 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 = new J3dMessage(); + 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 + // Fix for issue 66 : Since view might not been set yet, + // we have to use pendingView instead. + J3dMessage createMessage = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDER; + createMessage.type = J3dMessage.RENDER_OFFSCREEN; + createMessage.universe = this.pendingView.universe; + createMessage.view = this.pendingView; + 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 postSwap 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() { + + if (!offScreen) { + throw new IllegalStateException(J3dI18N.getString("Canvas3D1")); + } + + if (Thread.currentThread() instanceof Renderer) { + throw new IllegalStateException(J3dI18N.getString("Canvas3D31")); + } + + 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 + * Component.setLocation for on-screen Canvas3D + * objects. The default location is (0,0). + * + * @param x the x coordinate of the upper-left corner of + * the new location. + * @param y the y 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 + * Component.setLocation 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 + * Component.getLocation 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 + * Component.getLocation for on-screen Canvas3D + * objects. This version of getOffScreenLocation 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 rv is null, a new Point is allocated. + * + * @return rv + * + * @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() { + + ImageComponent2DRetained icRetained = (ImageComponent2DRetained)offScreenBuffer.retained; + boolean isByRef = icRetained.isByReference(); + ImageComponentRetained.ImageData imageData = icRetained.getImageData(false); + + if(!isByRef) { + // If icRetained has a null image ( BufferedImage) + if (imageData == null) { + assert (!isByRef); + icRetained.createBlankImageData(); + imageData = icRetained.getImageData(false); + } + // Check for possible format conversion in imageData + else { + // Format convert imageData if format is unsupported. + icRetained.evaluateExtensions(this); + } + // read the image from the offscreen buffer + readOffScreenBuffer(ctx, icRetained.getImageFormatTypeIntValue(false), + icRetained.getImageDataTypeIntValue(), imageData.get(), + offScreenCanvasSize.width, offScreenCanvasSize.height); + + } else { + icRetained.geomLock.getLock(); + // Create a copy of format converted image in imageData if format is unsupported. + icRetained.evaluateExtensions(this); + + // read the image from the offscreen buffer + readOffScreenBuffer(ctx, icRetained.getImageFormatTypeIntValue(false), + icRetained.getImageDataTypeIntValue(), imageData.get(), + offScreenCanvasSize.width, offScreenCanvasSize.height); + + // For byRef, we might have to copy buffer back into + // the user's referenced ImageComponent2D + if(!imageData.isDataByRef()) { + if(icRetained.isImageTypeSupported()) { + icRetained.copyToRefImage(0); + } else { + // This method only handle RGBA conversion. + icRetained.copyToRefImageWithFormatConversion(0); + } + } + + icRetained.geomLock.unLock(); + } + } + + /** + * 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 flush(true) 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 != null) && (view != null)) { + synchronized (drawingSurfaceObject) { + if (validCtx) { + if (!drawingSurfaceObject.renderLock()) { + graphicsContext3D.runMonitor(J3dThread.NOTIFY); + return; + } + this.syncRender(ctx, true); + swapBuffers(ctx, drawable); + drawingSurfaceObject.unLock(); + } + } + } + } catch (NullPointerException ne) { + drawingSurfaceObject.unLock(); + } + } + // Increment the elapsedFrame for the behavior structure + // to trigger any interpolators + view.universe.behaviorStructure.incElapsedFrames(); + graphicsContext3D.runMonitor(J3dThread.NOTIFY); + } + + /** + * Wrapper for native createNewContext method. + */ + Context createNewContext(Context shareCtx, boolean isSharedCtx) { + Context retVal = createNewContext( + this.drawable, + shareCtx, isSharedCtx, + this.offScreen); + // compute the max available texture units + maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits); + // reset 'antialiasingSet' if new context is created for an already existing Canvas3D, + // e.g. resizing offscreen Canvas3D + antialiasingSet = false; + + return retVal; + } + + /** + * Make the context associated with the specified canvas current. + */ + final void makeCtxCurrent() { + makeCtxCurrent(ctx, drawable); + } + + /** + * Make the specified context current. + */ + final void makeCtxCurrent(Context ctx) { + makeCtxCurrent(ctx, drawable); + } + + final void makeCtxCurrent(Context ctx, Drawable drawable) { + if (ctx != screen.renderer.currentCtx || drawable != screen.renderer.currentDrawable) { + if (!drawingSurfaceObject.isLocked()) { + drawingSurfaceObject.renderLock(); + useCtx(ctx, drawable); + drawingSurfaceObject.unLock(); + } else { + useCtx(ctx, drawable); + } + screen.renderer.currentCtx = ctx; + screen.renderer.currentDrawable = drawable; + } + } + + // Give the pipeline a chance to release the context; the Pipeline may + // or may not ignore this call. + void releaseCtx() { + if (screen.renderer.currentCtx != null) { + boolean needLock = !drawingSurfaceObject.isLocked(); + if (needLock) { + drawingSurfaceObject.renderLock(); + } + if (releaseCtx(screen.renderer.currentCtx)) { + screen.renderer.currentCtx = null; + screen.renderer.currentDrawable = null; + } + if (needLock) { + drawingSurfaceObject.unLock(); + } + } + } + + + /** + * 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[0] |= EYE_IN_IMAGE_PLATE_DIRTY; + cvDirtyMask[1] |= 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[0] |= EYE_IN_IMAGE_PLATE_DIRTY; + cvDirtyMask[1] |= 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 + */ + // XXXX: 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 + * X, Y, and Z 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 X, Y, and Z 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) { + 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; + canvasViewCacheFrustum = null; + } else { + + canvasViewCache = new CanvasViewCache(this, + screen.screenViewCache, + view.viewCache); + // Issue 109 : construct a separate canvasViewCache for + // computing view frustum + canvasViewCacheFrustum = new CanvasViewCache(this, + screen.screenViewCache, + view.viewCache); + synchronized (dirtyMaskLock) { + cvDirtyMask[0] = VIEW_INFO_DIRTY; + cvDirtyMask[1] = VIEW_INFO_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: + *

    + * + * ((Boolean)queryProperties(). + * get("stereoAvailable")). + * booleanValue() + * + *
+ * + * @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[0] |= STEREO_DIRTY; + cvDirtyMask[1] |= 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. + *

+ * 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[0] |= MONOSCOPIC_VIEW_POLICY_DIRTY; + cvDirtyMask[1] |= 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: + *

    + * + * ((Boolean)queryProperties(). + * get("doubleBufferAvailable")). + * booleanValue() + * + *
+ * + * @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: + *
    + * + * ((Boolean)queryProperties(). + * get("sceneAntialiasingAvailable")). + * booleanValue() + * + *
+ * + * @return a flag indicating whether scene antialiasing is available. + */ + public boolean getSceneAntialiasingAvailable() { + return ((Boolean)queryProperties().get("sceneAntialiasingAvailable")). + booleanValue(); + } + + + /** + * Returns a flag indicating whether or not the specified shading + * language is supported. A ShaderError will be generated if an + * unsupported shading language is used. + * + * @param shadingLanguage the shading language being queried, one of: + * Shader.SHADING_LANGUAGE_GLSL or + * Shader.SHADING_LANGUAGE_CG. + * + * @return true if the specified shading language is supported, + * false otherwise. + * + * @since Java 3D 1.4 + */ + public boolean isShadingLanguageSupported(int shadingLanguage) { + // Call queryProperties to ensure that the shading language flags are valid + queryProperties(); + + if (shadingLanguage == Shader.SHADING_LANGUAGE_GLSL) + return shadingLanguageGLSL; + + return false; + } + + + /** + * 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. + * + *

+ * The currently defined keys are: + * + *

+ *

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Key (String)Value Type
    shadingLanguageCgBoolean
    shadingLanguageGLSLBoolean
    doubleBufferAvailableBoolean
    stereoAvailableBoolean
    sceneAntialiasingAvailableBoolean
    sceneAntialiasingNumPassesInteger
    stencilSizeInteger
    texture3DAvailableBoolean
    textureColorTableSizeInteger
    textureLodRangeAvailableBoolean
    textureLodOffsetAvailableBoolean
    textureWidthMaxInteger
    textureHeightMaxInteger
    textureBoundaryWidthMaxInteger
    textureEnvCombineAvailableBoolean
    textureCombineDot3AvailableBoolean
    textureCombineSubtractAvailableBoolean
    textureCoordSetsMaxInteger
    textureUnitStateMaxInteger
    textureImageUnitsMaxInteger
    textureImageUnitsVertexMaxInteger
    textureImageUnitsCombinedMaxInteger
    textureCubeMapAvailableBoolean
    textureDetailAvailableBoolean
    textureSharpenAvailableBoolean
    textureFilter4AvailableBoolean
    textureAnisotropicFilterDegreeMaxFloat
    textureNonPowerOfTwoAvailableBoolean
    vertexAttrsMaxInteger
    compressedGeometry.majorVersionNumberInteger
    compressedGeometry.minorVersionNumberInteger
    compressedGeometry.minorMinorVersionNumberInteger
    native.versionString
    + *
+ * + *

+ * The descriptions of the values returned for each key are as follows: + * + *

+ *

    + *
  • + * shadingLanguageCg + *
      + * A Boolean indicating whether or not Cg shading Language + * is available for this Canvas3D. + *
    + *
  • + * + *
  • + * shadingLanguageGLSL + *
      + * A Boolean indicating whether or not GLSL shading Language + * is available for this Canvas3D. + *
    + *
  • + * + *
  • + * doubleBufferAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * stereoAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * sceneAntialiasingAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * sceneAntialiasingNumPasses + *
      + * 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. + *
    + *
  • + * + *
  • + * stencilSize + *
      + * An Integer indicating the number of stencil bits that are available + * for this Canvas3D. + *
    + *
  • + * + *
  • + * texture3DAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureColorTableSize + *
      + * 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. + *
    + *
  • + * + *
  • + * textureLodRangeAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureLodOffsetAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureWidthMax + *
      + * 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. + *
    + *
  • + * + *
  • + * textureHeightMax + *
      + * 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. + *
    + *
  • + * + *
  • + * textureBoundaryWidthMax + *
      + * 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. + *
    + *
  • + * + *
  • + * textureEnvCombineAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureCombineDot3Available + *
      + * 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. + *
    + *
  • + * + *
  • + * textureCombineSubtractAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureCoordSetsMax + *
      + * An Integer indicating the maximum number of texture coordinate sets + * supported by the underlying rendering layer. + *
    + *
  • + * + *
  • + * textureUnitStateMax + *
      + * An Integer indicating the maximum number of fixed-function texture units + * supported by the underlying rendering layer. If the number of + * application-sepcified texture unit states exceeds the maximum number + * for a Canvas3D, and the fixed-function rendering pipeline is used, then + * the texture will be effectively disabled for that Canvas3D. + *
    + *
  • + * + *
  • + * textureImageUnitsMax + *
      + * An Integer indicating the maximum number of texture image units + * that can be accessed by the fragment shader when programmable shaders + * are used. + *
    + *
  • + * + *
  • + * textureImageUnitsVertexMax + *
      + * An Integer indicating the maximum number of texture image units + * that can be accessed by the vertex shader when programmable shaders + * are used. + *
    + *
  • + * + *
  • + * textureImageUnitsCombinedMax + *
      + * An Integer indicating the combined maximum number of texture image units + * that can be accessed by the vertex shader and the fragment shader when + * programmable shaders are used. + *
    + *
  • + * + *
  • + * textureCubeMapAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureDetailAvailable + *
      + * 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. + * As of Java 3D 1.5, this property is always false. + *
    + *
  • + * + *
  • + * textureSharpenAvailable + *
      + * 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. + *
    + *
  • + * + *
  • + * textureFilter4Available + *
      + * 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. + *
    + *
  • + * + *
  • + * textureAnisotropicFilterDegreeMax + *
      + * 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. + *
    + *
  • + + *
  • + * textureNonPowerOfTwoAvailable + *
      + * A Boolean indicating whether or not texture dimensions that are + * not powers of two are supported for + * for this Canvas3D. If it indicates false, then textures with + * non power of two sizes will be ignored. Set the property + * j3d.textureEnforcePowerOfTwo to revert to the pre-1.5 behavior + * of throwing exceptions for non power of two textures. + *
    + *
  • + * + *
  • + * vertexAttrsMax + *
      + * An Integer indicating the maximum number of vertex attributes + * supported by the underlying rendering layer. This is in addition to + * the vertex coordinate (position), color, normal, and so forth. + *
    + *
  • + * + *
  • + * compressedGeometry.majorVersionNumber
    + * compressedGeometry.minorVersionNumber
    + * compressedGeometry.minorMinorVersionNumber + *
      + * Integers indicating the major, minor, and minor-minor + * version numbers, respectively, of the version of compressed + * geometry supported by this version of Java 3D. + *
    + *
  • + * + *
  • + * native.version + *
      + * A String indicating the version number of the native graphics + * library. The format of this string is defined by the native + * library. + *
    + *
  • + *
+ * + * @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 == null) { + createDummyCtx = true; + } + } + + if (createDummyCtx) { + GraphicsConfigTemplate3D.setQueryProps(this); + } + + //create query Properties + createQueryProps(); + } + + if (fatalError) { + throw new IllegalStateException(J3dI18N.getString("Canvas3D29")); + } + + 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(drawable, offScreen, 1, 1); + // compute the max available texture units + maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits); + } + + /** + * 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("stencilSize"); + // Return the actual stencil size if the user owns it, otherwise + // return 0 + if (userStencilAvailable) { + values.add(new Integer(actualStencilSize)); + } else { + values.add(new Integer(0)); + } + + 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("texture3DWidthMax"); + values.add(new Integer(texture3DWidthMax)); + + keys.add("texture3DHeightMax"); + values.add(new Integer(texture3DHeightMax)); + + keys.add("texture3DDepthMax"); + values.add(new Integer(texture3DDepthMax)); + + 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("textureNonPowerOfTwoAvailable"); + values.add(new Boolean( + (textureExtendedFeatures & TEXTURE_NON_POWER_OF_TWO) != 0)); + + keys.add("textureAutoMipMapGenerationAvailable"); + values.add(new Boolean( + (textureExtendedFeatures & TEXTURE_AUTO_MIPMAP_GENERATION) != 0)); + + keys.add("textureCoordSetsMax"); + values.add(new Integer(maxTexCoordSets)); + + keys.add("textureUnitStateMax"); + values.add(new Integer(maxTextureUnits)); + + keys.add("textureImageUnitsMax"); + values.add(new Integer(maxTextureImageUnits)); + + keys.add("textureImageUnitsVertexMax"); + values.add(new Integer(maxVertexTextureImageUnits)); + + keys.add("textureImageUnitsCombinedMax"); + values.add(new Integer(maxCombinedTextureImageUnits)); + + keys.add("vertexAttrsMax"); + values.add(new Integer(maxVertexAttrs)); + + keys.add("shadingLanguageGLSL"); + values.add(new Boolean(shadingLanguageGLSL)); + + keys.add("native.version"); + values.add(nativeGraphicsVersion); + + keys.add("native.vendor"); + values.add(nativeGraphicsVendor); + + keys.add("native.renderer"); + values.add(nativeGraphicsRenderer); + + // Now Create read-only properties object + queryProps = new J3dQueryProps(keys, values); + } + + + /** + * Update the view cache associated with this canvas. + */ + void updateViewCache(boolean flag, CanvasViewCache cvc, + BoundingBox frustumBBox, boolean doInfinite) { + + assert cvc == null; + synchronized(cvLock) { + if (firstPaintCalled && (canvasViewCache != null)) { + assert canvasViewCacheFrustum != null; + // Issue 109 : choose the appropriate cvCache + if (frustumBBox != null) { + canvasViewCacheFrustum.snapshot(true); + canvasViewCacheFrustum.computeDerivedData(flag, null, + frustumBBox, doInfinite); + } else { + canvasViewCache.snapshot(false); + canvasViewCache.computeDerivedData(flag, null, + null, 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(Context 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; + } + +// reset all attributes so that everything e.g. display list, +// texture will recreate again in the next frame +void resetRendering() { + reset(); + + synchronized (dirtyMaskLock) { + cvDirtyMask[0] |= VIEW_INFO_DIRTY; + cvDirtyMask[1] |= VIEW_INFO_DIRTY; + } + +} + + void reset() { + int i; + currentAppear = new AppearanceRetained(); + currentMaterial = new MaterialRetained(); + viewFrustum = new CachedFrustum(); + canvasDirty = 0xffff; + lightBin = null; + environmentSet = null; + attributeBin = null; + shaderBin = null; + textureBin = null; + renderMolecule = null; + polygonAttributes = null; + lineAttributes = null; + pointAttributes = null; + material = null; + enableLighting = false; + transparency = null; + coloringAttributes = null; + shaderProgram = 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; + 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; + } + + // Issue 362 - need to reset display lists and ctxTimeStamp in this + // method, so that display lists will be recreated when canvas is + // removed from a view and then added back into a view with another + // canvas + needToRebuildDisplayList = true; + ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); + } + + +void resetImmediateRendering() { + 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(); + makeCtxCurrent(); + synchronized (dirtyMaskLock) { + cvDirtyMask[0] |= VIEW_INFO_DIRTY; + cvDirtyMask[1] |= VIEW_INFO_DIRTY; + } + needToRebuildDisplayList = true; + + ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp(); +} + +@Override +public Point getLocationOnScreen() { + try { + return super.getLocationOnScreen(); + } + catch (IllegalComponentStateException e) {} + + return new Point(); +} + + void setProjectionMatrix(Context ctx, Transform3D projTrans) { + this.projTrans = projTrans; + setProjectionMatrix(ctx, projTrans.mat); + } + + void setModelViewMatrix(Context ctx, double[] viewMatrix, Transform3D mTrans) { + setModelViewMatrix(ctx, viewMatrix, mTrans.mat); + if (!useStereo) { + this.modelMatrix = mTrans; + } else { + // TODO : This seems wrong to do only for the right eye. + // A possible approach is to invalidate the cache at begin of + // each eye. + if (rightStereoPass) { + // Only set cache in right stereo pass, otherwise + // if the left stereo 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 setNumActiveTexUnit(int n) { + numActiveTexUnit = n; + } + + int getNumActiveTexUnit() { + return numActiveTexUnit; + } + + void setLastActiveTexUnit(int n) { + lastActiveTexUnit = n; + } + + int getLastActiveTexUnit() { + return lastActiveTexUnit; + } + + // Create the texture state array + void createTexUnitState() { + texUnitState = new TextureUnitStateRetained[maxAvailableTextureUnits]; + for (int t = 0; t < maxAvailableTextureUnits; t++) { + texUnitState[t] = new TextureUnitStateRetained(); + texUnitState[t].texture = null; + texUnitState[t].mirror = null; + } + } + + /** + * Enable separate specular color if it is not overriden by the + * property j3d.disableSeparateSpecular. + */ + void enableSeparateSpecularColor() { + boolean enable = !VirtualUniverse.mc.disableSeparateSpecularColor; + updateSeparateSpecularColorEnable(ctx, enable); + } + + // Send a createOffScreenBuffer message to Renderer (via + // MasterControl) and wait for it to be done + private void sendCreateOffScreenBuffer() { + // Wait for the buffer to be created unless called from + // a Behavior or from a Rendering thread + if (!(Thread.currentThread() instanceof BehaviorScheduler) && + !(Thread.currentThread() instanceof Renderer)) { + + offScreenBufferPending = true; + } + + // Send message to Renderer thread to perform createOffScreenBuffer. + VirtualUniverse.mc.sendCreateOffScreenBuffer(this); + + // Wait for off-screen buffer to be created + while (offScreenBufferPending) { + // Issue 364: create master control thread if needed + VirtualUniverse.mc.createMasterControlThread(); + MasterControl.threadYield(); + } + } + + // Send a destroyOffScreenBuffer message to Renderer (via + // MasterControl) and wait for it to be done + private void sendDestroyCtxAndOffScreenBuffer() { + // Wait for the buffer to be destroyed unless called from + // a Behavior or from a Rendering thread + Thread currentThread = Thread.currentThread(); + if (!(currentThread instanceof BehaviorScheduler) && + !(currentThread instanceof Renderer)) { + + offScreenBufferPending = true; + } + + // Fix for Issue 18 and Issue 175 + // Send message to Renderer thread to perform remove Ctx and destroyOffScreenBuffer. + + VirtualUniverse.mc.sendDestroyCtxAndOffScreenBuffer(this); + + // Wait for ctx and off-screen buffer to be destroyed + while (offScreenBufferPending) { + // Issue 364: create master control thread if needed + VirtualUniverse.mc.createMasterControlThread(); + MasterControl.threadYield(); + } + } + + // Send a allocateCanvasId message to Renderer (via MasterControl) without + // waiting for it to be done + private void sendAllocateCanvasId() { + // Send message to Renderer thread to allocate a canvasId + VirtualUniverse.mc.sendAllocateCanvasId(this); + } + + // Send a freeCanvasId message to Renderer (via MasterControl) without + // waiting for it to be done + private void sendFreeCanvasId() { + // Send message to Renderer thread to free the canvasId + VirtualUniverse.mc.sendFreeCanvasId(this); + } + + private void removeCtx() { + + if ((screen != null) && + (screen.renderer != null) && + (ctx != null)) { + VirtualUniverse.mc.postRequest(MasterControl.FREE_CONTEXT, + new Object[]{this, + Long.valueOf(0L), + drawable, + ctx}); + // Fix for Issue 19 + // Wait for the context to be freed unless called from + // a Behavior or from a Rendering thread + Thread currentThread = Thread.currentThread(); + if (!(currentThread instanceof BehaviorScheduler) && + !(currentThread instanceof Renderer)) { + while (ctxTimeStamp != 0) { + MasterControl.threadYield(); + } + } + ctx = null; + } + } + + /** + * 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 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 & ShaderBin if neccessary + // according to the stateUpdateMask + + static int ENV_STATE_MASK = (1 << LIGHTBIN_BIT) | + (1 << ENVIRONMENTSET_BIT) | + (1 << ATTRIBUTEBIN_BIT) | + (1 << SHADERBIN_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); + } + + if ((stateUpdateMask & (1 << SHADERBIN_BIT)) != 0) { + ((ShaderBin) + curStateToUpdate[SHADERBIN_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 dirtyBits) { + + + if (stateUpdateMask == 0) + return; + + updateEnvState(); + + if ((stateUpdateMask & (1 << TEXTUREBIN_BIT)) != 0) { + ((TextureBin) + curStateToUpdate[TEXTUREBIN_BIT]).updateAttributes(this); + } + + 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; + } + + + // This method updates this Texture2D for raster. + // Note : No multi-texture is not used. + void updateTextureForRaster(Texture2DRetained texture) { + + // Setup texture and texture attributes for texture unit 0. + Pipeline.getPipeline().updateTextureUnitState(ctx, 0, true); + setLastActiveTexUnit(0); + setNumActiveTexUnit(1); + + texture.updateNative(this); + resetTexCoordGeneration(ctx); + resetTextureAttributes(ctx); + + for(int i=1; i < maxTextureUnits; i++) { + resetTexture(ctx, i); + } + + // set the active texture unit back to 0 + activeTextureUnit(ctx, 0); + + // Force the next textureBin to reload. + canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY; + } + + void restoreTextureBin() { + + // Need to check TextureBin's shaderBin for null + // TextureBin can get clear() if there isn't any RM under it. + if((textureBin != null) && (textureBin.shaderBin != null)) { + textureBin.updateAttributes(this); + } + } + + void textureFill(RasterRetained raster, Point2d winCoord, + float mapZ, float alpha) { + + int winWidth = canvasViewCache.getCanvasWidth(); + int winHeight = canvasViewCache.getCanvasHeight(); + + int rasterImageWidth = raster.image.width; + int rasterImageHeight = raster.image.height; + + float texMinU = 0, texMinV = 0, texMaxU = 0, texMaxV = 0; + float mapMinX = 0, mapMinY = 0, mapMaxX = 0, mapMaxY = 0; + + Point rasterSrcOffset = new Point(); + raster.getSrcOffset(rasterSrcOffset); + + Dimension rasterSize = new Dimension(); + raster.getSize(rasterSize); + +// System.err.println("rasterImageWidth " + rasterImageWidth + " rasterImageHeight " + rasterImageHeight); +// System.err.println("rasterSrcOffset " + rasterSrcOffset + " rasterSize " + rasterSize); + + int rasterMinX = rasterSrcOffset.x; + int rasterMaxX = rasterSrcOffset.x + rasterSize.width; + int rasterMinY = rasterSrcOffset.y; + int rasterMaxY = rasterSrcOffset.y + rasterSize.height; + + if ((rasterMinX >= rasterImageWidth) || (rasterMinY >= rasterImageHeight) || + (rasterMaxX <= 0) || (rasterMaxY <= 0)) { + return; + } + + if (rasterMinX < 0) { + rasterMinX = 0; + } + if (rasterMinY < 0) { + rasterMinY = 0; + } + + if (rasterMaxX > rasterImageWidth) { + rasterMaxX = rasterImageWidth; + } + + if (rasterMaxY > rasterImageHeight) { + rasterMaxY = rasterImageHeight; + } + + texMinU = (float) rasterMinX / (float) rasterImageWidth; + texMaxU = (float) rasterMaxX / (float) rasterImageWidth; + mapMinX = (float) winCoord.x / (float) winWidth; + mapMaxX = (float) (winCoord.x + (rasterMaxX - rasterMinX)) / (float) winWidth; + + if (raster.image.isYUp()) { + texMinV = (float) rasterMinY / (float) rasterImageHeight; + texMaxV = (float) rasterMaxY / (float) rasterImageHeight; + } else { + // System.err.println("In yUp is false case"); + texMinV = 1.0f - (float) rasterMaxY / (float) rasterImageHeight; + texMaxV = 1.0f - (float) rasterMinY / (float) rasterImageHeight; + } + + mapMinY = 1.0f - ((float) (winCoord.y + (rasterMaxY - rasterMinY)) / (float) winHeight); + mapMaxY = 1.0f - ((float) winCoord.y / (float) winHeight); + + textureFillRaster(ctx, texMinU, texMaxU, texMinV, texMaxV, + mapMinX, mapMaxX, mapMinY, mapMaxY, mapZ, alpha, raster.image.useBilinearFilter()); + + } + + void textureFill(BackgroundRetained bg, int winWidth, int winHeight) { + + final int maxX = bg.image.width; + final int maxY = bg.image.height; + +// System.err.println("maxX " + maxX + " maxY " + maxY); + + float xzoom = (float)winWidth / maxX; + float yzoom = (float)winHeight / maxY; + float zoom = 0; + float texMinU = 0, texMinV = 0, texMaxU = 0, texMaxV = 0, adjustV = 0; + float mapMinX = 0, mapMinY = 0, mapMaxX = 0, mapMaxY = 0; + float halfWidth = 0, halfHeight = 0; + + switch (bg.imageScaleMode) { + case Background.SCALE_NONE: + texMinU = 0.0f; + texMinV = 0.0f; + texMaxU = 1.0f; + texMaxV = 1.0f; + halfWidth = (float)winWidth/2.0f; + halfHeight = (float)winHeight/2.0f; + mapMinX = (float) ((0 - halfWidth)/halfWidth); + mapMinY = (float) ((0 - halfHeight)/halfHeight); + mapMaxX = (float) ((maxX - halfWidth)/halfWidth); + mapMaxY = (float) ((maxY - halfHeight)/halfHeight); + adjustV = ((float)winHeight - (float)maxY)/halfHeight; + mapMinY += adjustV; + mapMaxY += adjustV; + break; + case Background.SCALE_FIT_MIN: + zoom = Math.min(xzoom, yzoom); + texMinU = 0.0f; + texMinV = 0.0f; + texMaxU = 1.0f; + texMaxV = 1.0f; + mapMinX = -1.0f; + mapMaxY = 1.0f; + if (xzoom < yzoom) { + mapMaxX = 1.0f; + mapMinY = -1.0f + 2.0f * ( 1.0f - zoom * (float)maxY/(float) winHeight ); + } else { + mapMaxX = -1.0f + zoom * (float)maxX/winWidth * 2; + mapMinY = -1.0f; + } + break; + case Background.SCALE_FIT_MAX: + zoom = Math.max(xzoom, yzoom); + mapMinX = -1.0f; + mapMinY = -1.0f; + mapMaxX = 1.0f; + mapMaxY = 1.0f; + if (xzoom < yzoom) { + texMinU = 0.0f; + texMinV = 0.0f; + texMaxU = (float)winWidth/maxX/zoom; + texMaxV = 1.0f; + } else { + texMinU = 0.0f; + texMinV = 1.0f - (float)winHeight/maxY/zoom; + texMaxU = 1.0f; + texMaxV = 1.0f; + } + break; + case Background.SCALE_FIT_ALL: + texMinU = 0.0f; + texMinV = 0.0f; + texMaxU = 1.0f; + texMaxV = 1.0f; + mapMinX = -1.0f; + mapMinY = -1.0f; + mapMaxX = 1.0f; + mapMaxY = 1.0f; + break; + case Background.SCALE_REPEAT: + + texMinU = 0.0f; + texMinV = - yzoom; + texMaxU = xzoom; + texMaxV = 0.0f; + mapMinX = -1.0f; + mapMinY = -1.0f; + mapMaxX = 1.0f; + mapMaxY = 1.0f; + break; + case Background.SCALE_NONE_CENTER: + // TODO : Why is there a zoom ? + if(xzoom >= 1.0f){ + texMinU = 0.0f; + texMaxU = 1.0f; + mapMinX = -(float)maxX/winWidth; + mapMaxX = (float)maxX/winWidth; + } else { + texMinU = 0.5f - (float)winWidth/maxX/2; + texMaxU = 0.5f + (float)winWidth/maxX/2; + mapMinX = -1.0f; + mapMaxX = 1.0f; + } + if (yzoom >= 1.0f) { + texMinV = 0.0f; + texMaxV = 1.0f; + mapMinY = -(float)maxY/winHeight; + mapMaxY = (float)maxY/winHeight; + } else { + texMinV = 0.5f - (float)winHeight/maxY/2; + texMaxV = 0.5f + (float)winHeight/maxY/2; + mapMinY = -1.0f; + mapMaxY = 1.0f; + } + break; + } + +// System.err.println("Java 3D : mapMinX " + mapMinX + " mapMinY " + mapMinY + +// " mapMaxX " + mapMaxX + " mapMaxY " + mapMaxY); + textureFillBackground(ctx, texMinU, texMaxU, texMinV, texMaxV, + mapMinX, mapMaxX, mapMinY, mapMaxY, bg.image.useBilinearFilter()); + + } + + + void clear(BackgroundRetained bg, int winWidth, int winHeight) { + + // Issue 239 - clear stencil if requested and available + // Note that this is a partial solution, since we eventually want an API + // to control this. + boolean clearStencil = VirtualUniverse.mc.stencilClear && + userStencilAvailable; + + clear(ctx, bg.color.x, bg.color.y, bg.color.z, clearStencil); + + // TODO : This is a bug on not mirror bg. Will fix this as a bug after 1.5 beta. + // For now, as a workaround, we will check bg.image and bg.image.imageData not null. + if((bg.image != null) && (bg.image.imageData != null)) { + // setup Texture pipe. + updateTextureForRaster(bg.texture); + + textureFill(bg, winWidth, winHeight); + + // Restore texture pipe. + restoreTextureBin(); + } + } + + /** + * 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, TextureRetained 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(Context ctx) { + Iterator it; + int val; + + // free resource for those canvases that + // don't use shared ctx + if (displayListResourceFreeList.size() > 0) { + for (it = displayListResourceFreeList.iterator(); it.hasNext();) { + val = it.next().intValue(); + if (val <= 0) { + continue; + } + Canvas3D.freeDisplayList(ctx, val); + } + displayListResourceFreeList.clear(); + } + + if (textureIdResourceFreeList.size() > 0) { + for (it = textureIdResourceFreeList.iterator(); it.hasNext();) { + val = it.next().intValue(); + if (val <= 0) { + continue; + } + if (val >= textureIDResourceTable.size()) { + System.err.println("Error in freeResourcesInFreeList : ResourceIDTableSize = " + + textureIDResourceTable.size() + + " val = " + val); + } else { + TextureRetained tex = textureIDResourceTable.get(val); + if (tex != null) { + synchronized (tex.resourceLock) { + tex.resourceCreationMask &= ~canvasBit; + if (tex.resourceCreationMask == 0) { + tex.freeTextureId(val); + } + } + } + + textureIDResourceTable.set(val, null); + } + Canvas3D.freeTexture(ctx, val); + } + textureIdResourceFreeList.clear(); + } + } + + void freeContextResources(Renderer rdr, boolean freeBackground, + Context ctx) { + TextureRetained tex; + + // Just return if we don't have a valid renderer or context + if (rdr == null || ctx == null) { + return; + } + + if (freeBackground) { + // Dispose of Graphics2D Texture + if (graphics2D != null) { + graphics2D.dispose(); + } + } + + for (int id = textureIDResourceTable.size()-1; id >= 0; id--) { + tex = textureIDResourceTable.get(id); + if (tex == null) { + continue; + } + + // Issue 403 : this assertion doesn't hold in some cases + // TODO KCR : determine why this is the case +// assert id == ((TextureRetained)obj).objectId; + + Canvas3D.freeTexture(ctx, id); + synchronized (tex.resourceLock) { + tex.resourceCreationMask &= ~canvasBit; + if (tex.resourceCreationMask == 0) { + + tex.freeTextureId(id); + } + } + } + textureIDResourceTable.clear(); + + freeAllDisplayListResources(ctx); + } + + void freeAllDisplayListResources(Context ctx) { + if ((view != null) && (view.renderBin != null)) { + view.renderBin.freeAllDisplayListResources(this, ctx); + 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; + } + } + } + + } + + + // ***************************************************************** + // Wrappers for native methods go below here + // ***************************************************************** + + // This is the native method for creating the underlying graphics context. + private Context createNewContext(Drawable drawable, + Context shareCtx, boolean isSharedCtx, + boolean offScreen) { + return Pipeline.getPipeline().createNewContext(this, drawable, + shareCtx, isSharedCtx, + offScreen); + } + + private void createQueryContext(Drawable drawable, + boolean offScreen, int width, int height) { + Pipeline.getPipeline().createQueryContext(this, drawable, + offScreen, width, height); + } + + // This is the native for creating offscreen buffer + Drawable createOffScreenBuffer(Context ctx, int width, int height) { + return Pipeline.getPipeline().createOffScreenBuffer(this, + ctx, width, height); + } + + void destroyOffScreenBuffer(Context ctx, Drawable drawable) { + assert drawable != null; + Pipeline.getPipeline().destroyOffScreenBuffer(this, ctx, drawable); + } + + // This is the native for reading the image from the offscreen buffer + private void readOffScreenBuffer(Context ctx, int format, int type, Object data, int width, int height) { + Pipeline.getPipeline().readOffScreenBuffer(this, ctx, format, type, data, width, height); + } + +// The native method for swapBuffers +void swapBuffers(Context ctx, Drawable drawable) { + Pipeline.getPipeline().swapBuffers(this, ctx, drawable); +} + + // ----------------------------------------------------------------------------- + + // native method for setting Material when no material is present + void updateMaterial(Context ctx, float r, float g, float b, float a) { + Pipeline.getPipeline().updateMaterialColor(ctx, r, g, b, a); + } + + static void destroyContext(Drawable drawable, Context ctx) { + Pipeline.getPipeline().destroyContext(drawable, ctx); + } + + // This is the native method for doing accumulation. + void accum(Context ctx, float value) { + Pipeline.getPipeline().accum(ctx, value); + } + + // This is the native method for doing accumulation return. + void accumReturn(Context ctx) { + Pipeline.getPipeline().accumReturn(ctx); + } + + // This is the native method for clearing the accumulation buffer. + void clearAccum(Context ctx) { + Pipeline.getPipeline().clearAccum(ctx); + } + + // This is the native method for getting the number of lights the underlying + // native library can support. + int getNumCtxLights(Context ctx) { + return Pipeline.getPipeline().getNumCtxLights(ctx); + } + + // Native method for decal 1st child setup + boolean decal1stChildSetup(Context ctx) { + return Pipeline.getPipeline().decal1stChildSetup(ctx); + } + + // Native method for decal nth child setup + void decalNthChildSetup(Context ctx) { + Pipeline.getPipeline().decalNthChildSetup(ctx); + } + + // Native method for decal reset + void decalReset(Context ctx, boolean depthBufferEnable) { + Pipeline.getPipeline().decalReset(ctx, depthBufferEnable); + } + + // Native method for decal reset + void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable) { + Pipeline.getPipeline().ctxUpdateEyeLightingEnable(ctx, localEyeLightingEnable); + } + + // The following three methods are used in multi-pass case + + // native method for setting blend color + void setBlendColor(Context ctx, float red, float green, + float blue, float alpha) { + Pipeline.getPipeline().setBlendColor(ctx, red, green, + blue, alpha); + } + + // native method for setting blend func + void setBlendFunc(Context ctx, int src, int dst) { + Pipeline.getPipeline().setBlendFunc(ctx, src, dst); + } + + // native method for setting fog enable flag + void setFogEnableFlag(Context ctx, boolean enableFlag) { + Pipeline.getPipeline().setFogEnableFlag(ctx, enableFlag); + } + +boolean isAntialiasingSet() { + return antialiasingSet; +} + + // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported + void setFullSceneAntialiasing(Context ctx, boolean enable) { + Pipeline.getPipeline().setFullSceneAntialiasing(ctx, enable); + antialiasingSet = enable; + } + + // Native method to update separate specular color control + void updateSeparateSpecularColorEnable(Context ctx, boolean control) { + Pipeline.getPipeline().updateSeparateSpecularColorEnable(ctx, control); + } + + // True under Solaris, + // False under windows when display mode <= 8 bit + private boolean validGraphicsMode() { + return Pipeline.getPipeline().validGraphicsMode(); + } + + // native method for setting light enables + void setLightEnables(Context ctx, long enableMask, int maxLights) { + Pipeline.getPipeline().setLightEnables(ctx, enableMask, maxLights); + } + + // native method for setting scene ambient + void setSceneAmbient(Context ctx, float red, float green, float blue) { + Pipeline.getPipeline().setSceneAmbient(ctx, red, green, blue); + } + + // native method for disabling fog + void disableFog(Context ctx) { + Pipeline.getPipeline().disableFog(ctx); + } + + // native method for disabling modelClip + void disableModelClip(Context ctx) { + Pipeline.getPipeline().disableModelClip(ctx); + } + + // native method for setting default RenderingAttributes + void resetRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride) { + Pipeline.getPipeline().resetRenderingAttributes(ctx, + depthBufferWriteEnableOverride, + depthBufferEnableOverride); + } + + // native method for setting default texture + void resetTextureNative(Context ctx, int texUnitIndex) { + Pipeline.getPipeline().resetTextureNative(ctx, texUnitIndex); + } + + // native method for activating a particular texture unit + void activeTextureUnit(Context ctx, int texUnitIndex) { + Pipeline.getPipeline().activeTextureUnit(ctx, texUnitIndex); + } + + // native method for setting default TexCoordGeneration + void resetTexCoordGeneration(Context ctx) { + Pipeline.getPipeline().resetTexCoordGeneration(ctx); + } + + // native method for setting default TextureAttributes + void resetTextureAttributes(Context ctx) { + Pipeline.getPipeline().resetTextureAttributes(ctx); + } + + // native method for setting default PolygonAttributes + void resetPolygonAttributes(Context ctx) { + Pipeline.getPipeline().resetPolygonAttributes(ctx); + } + + // native method for setting default LineAttributes + void resetLineAttributes(Context ctx) { + Pipeline.getPipeline().resetLineAttributes(ctx); + } + + // native method for setting default PointAttributes + void resetPointAttributes(Context ctx) { + Pipeline.getPipeline().resetPointAttributes(ctx); + } + + // native method for setting default TransparencyAttributes + void resetTransparency(Context ctx, int geometryType, + int polygonMode, boolean lineAA, + boolean pointAA) { + Pipeline.getPipeline().resetTransparency(ctx, geometryType, + polygonMode, lineAA, + pointAA); + } + + // native method for setting default ColoringAttributes + void resetColoringAttributes(Context ctx, + float r, float g, + float b, float a, + boolean enableLight) { + Pipeline.getPipeline().resetColoringAttributes(ctx, + r, g, + b, a, + enableLight); + } + + /** + * This native method makes sure that the rendering for this canvas + * gets done now. + */ + void syncRender(Context ctx, boolean wait) { + Pipeline.getPipeline().syncRender(ctx, wait); + } + + // The native method that sets this ctx to be the current one + static boolean useCtx(Context ctx, Drawable drawable) { + return Pipeline.getPipeline().useCtx(ctx, drawable); + } + + // Give the Pipeline a chance to release the context. The return + // value indicates whether the context was released. + private boolean releaseCtx(Context ctx) { + return Pipeline.getPipeline().releaseCtx(ctx); + } + + void clear(Context ctx, float r, float g, float b, boolean clearStencil) { + Pipeline.getPipeline().clear(ctx, r, g, b, clearStencil); + } + + void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, boolean useBiliearFilter) { + Pipeline.getPipeline().textureFillBackground(ctx, texMinU, texMaxU, texMinV, texMaxV, + mapMinX, mapMaxX, mapMinY, mapMaxY, useBiliearFilter); + } + + void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, float mapZ, float alpha, boolean useBiliearFilter) { + Pipeline.getPipeline().textureFillRaster(ctx, texMinU, texMaxU, texMinV, texMaxV, + mapMinX, mapMaxX, mapMinY, mapMaxY, mapZ, alpha, useBiliearFilter); + } + + void executeRasterDepth(Context ctx, float posX, float posY, float posZ, + int srcOffsetX, int srcOffsetY, int rasterWidth, int rasterHeight, + int depthWidth, int depthHeight, int depthType, Object depthData) { + Pipeline.getPipeline().executeRasterDepth(ctx, posX, posY, posZ, + srcOffsetX, srcOffsetY, rasterWidth, rasterHeight, depthWidth, depthHeight, depthType, depthData); + } + + // The native method for setting the ModelView matrix. + void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix) { + Pipeline.getPipeline().setModelViewMatrix(ctx, viewMatrix, modelMatrix); + } + + // The native method for setting the Projection matrix. + void setProjectionMatrix(Context ctx, double[] projMatrix) { + Pipeline.getPipeline().setProjectionMatrix(ctx, projMatrix); + } + + // The native method for setting the Viewport. + void setViewport(Context ctx, int x, int y, int width, int height) { + Pipeline.getPipeline().resizeOffscreenLayer(this, width, height); + Pipeline.getPipeline().setViewport(ctx, x, y, width, height); + } + + // used for display Lists + void newDisplayList(Context ctx, int displayListId) { + Pipeline.getPipeline().newDisplayList(ctx, displayListId); + } + void endDisplayList(Context ctx) { + Pipeline.getPipeline().endDisplayList(ctx); + } + void callDisplayList(Context ctx, int id, boolean isNonUniformScale) { + Pipeline.getPipeline().callDisplayList(ctx, id, isNonUniformScale); + } + + static void freeDisplayList(Context ctx, int id) { + Pipeline.getPipeline().freeDisplayList(ctx, id); + } + static void freeTexture(Context ctx, int id) { + Pipeline.getPipeline().freeTexture(ctx, id); + } + + static int generateTexID(Context ctx) { + return Pipeline.getPipeline().generateTexID(ctx); + } + + void texturemapping(Context 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) { + Pipeline.getPipeline().texturemapping(ctx, + px, py, + xmin, ymin, xmax, ymax, + texWidth, texHeight, + rasWidth, + format, objectId, + image, + winWidth, winHeight); + } + + boolean initTexturemapping(Context ctx, int texWidth, + int texHeight, int objectId) { + return Pipeline.getPipeline().initTexturemapping(ctx, texWidth, + texHeight, objectId); + } + + + // 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. + void setRenderMode(Context ctx, int mode, boolean doubleBuffer) { + Pipeline.getPipeline().setRenderMode(ctx, mode, doubleBuffer); + } + + // Set glDepthMask. + void setDepthBufferWriteEnable(Context ctx, boolean mode) { + Pipeline.getPipeline().setDepthBufferWriteEnable(ctx, mode); + } + + // Methods to get actual capabilities from Canvas3D + + boolean hasDoubleBuffer() { + return Pipeline.getPipeline().hasDoubleBuffer(this); + } + + boolean hasStereo() { + return Pipeline.getPipeline().hasStereo(this); + } + + int getStencilSize() { + return Pipeline.getPipeline().getStencilSize(this); + } + + boolean hasSceneAntialiasingMultisample() { + return Pipeline.getPipeline().hasSceneAntialiasingMultisample(this); + } + + boolean hasSceneAntialiasingAccum() { + return Pipeline.getPipeline().hasSceneAntialiasingAccum(this); + } + +} diff --git a/src/javax/media/j3d/CanvasViewCache.java b/src/javax/media/j3d/CanvasViewCache.java new file mode 100644 index 0000000..0360d3c --- /dev/null +++ b/src/javax/media/j3d/CanvasViewCache.java @@ -0,0 +1,2044 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Rectangle; + +import javax.vecmath.Matrix4d; +import javax.vecmath.Point2d; +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.SingularMatrixException; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector4d; + +/** + * 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.err.println("Get canvas position and size"); + System.err.println("Before"); + System.err.println("Canvas pos = (" + awtCanvasX + ", " + + awtCanvasY + "), size = " + awtCanvasWidth + + "x" + awtCanvasHeight); + System.err.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.err.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(boolean computeFrustum) { + // Issue 109 : determine the the correct index to use -- either the + // Renderer or RenderBin + int dirtyIndex = computeFrustum ? + Canvas3D.RENDER_BIN_DIRTY_IDX : Canvas3D.RENDERER_DIRTY_IDX; + + synchronized (canvas.dirtyMaskLock) { + // Issue 109 : read/clear the dirty bits for the correct index + cvcDirtyMask = canvas.cvDirtyMask[dirtyIndex]; + canvas.cvDirtyMask[dirtyIndex] = 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.err.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) { + + // Issue 109 : determine the the correct index to use -- either the + // Renderer or RenderBin + int dirtyIndex = (frustumBBox != null) ? + Canvas3D.RENDER_BIN_DIRTY_IDX : Canvas3D.RENDERER_DIRTY_IDX; + int scrvcDirtyMask; + + // Issue 109 : read/clear the dirty bits for the correct index + synchronized (screenViewCache) { + scrvcDirtyMask = screenViewCache.scrvcDirtyMask[dirtyIndex]; + // 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[dirtyIndex] = 0; + } + } + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + if(cvcDirtyMask != 0) + System.err.println("cvcDirtyMask : " + cvcDirtyMask); + + if(scrvcDirtyMask != 0) + System.err.println("scrvcDirtyMask : "+ scrvcDirtyMask); + + if(viewCache.vcDirtyMask != 0) + System.err.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.err.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. + // XXXX: Peeking at the dirty flag is a hack. Need to revisit this. + boolean vprNotDirty = (viewCache.vpRetained.vprDirtyMask == 0); + + // Issue 131: If not manual, it has to be considered as an onscreen canvas. + if(!canvas.manualRendering && + (vprNotDirty) && + (cvcDirtyMask == 0) && + (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.err.println("vpcToVworld is \n" + vpcToVworld); + + try { + vworldToVpc.invert(vpcToVworld); + } + catch (SingularMatrixException e) { + vworldToVpc.setIdentity(); + //System.err.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); + + // Issue 109: cvc should *always* be null + assert cvc == null; + if(cvc != null) + copyComputedCanvasViewCache(cvc, doInfinite); + + canvas.canvasDirty |= Canvas3D.VIEW_MATRIX_DIRTY; + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) { + // Print some data : + System.err.println("useStereo = " + useStereo); + System.err.println("leftProjection:\n" + leftProjection); + System.err.println("rightProjection:\n " + rightProjection); + System.err.println("leftVpcToEc:\n" + leftVpcToEc); + System.err.println("rightVpcToEc:\n" + rightVpcToEc); + System.err.println("vpcToVworld:\n" + vpcToVworld); + System.err.println("vworldToVpc:\n" + vworldToVpc); + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + int i; + for (i = 0; i < leftFrustumPlanes.length; i++) { + System.err.println("leftFrustumPlanes " + i + " is " + + leftFrustumPlanes[i]); + } + + for (i = 0; i < rightFrustumPlanes.length; i++) { + System.err.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.err.println("Canvas pos = (" + awtCanvasX + ", " + + awtCanvasY + "), size = " + awtCanvasWidth + + "x" + awtCanvasHeight); + + System.err.println("Window LL corner (in plate coordinates): " + + "(" + physicalWindowXLeft + "," + physicalWindowYBottom + ")"); + + System.err.println("Window size (in plate coordinates): " + + "(" + physicalWindowWidth + "," + physicalWindowHeight + ")"); + + System.err.println("Window center (in plate coordinates): " + + physicalWindowCenter); + + System.err.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.err.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.err.println("viewCache.windowResizePolicy = " + + viewCache.windowResizePolicy); + System.err.println("physicalWindowWidth = " + physicalWindowWidth); + System.err.println("physicalScreenWidth = " + physicalScreenWidth); + System.err.println("windowScale = " + windowScale); + System.err.println("screenScale = " + screenScale); + System.err.println("viewPlatformScale = " + viewPlatformScale); + } + } + + private void cacheEyePosFixedField() { + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) + System.err.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.err.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.err.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.err.println("computeTrackedEyePosition:"); + System.err.println("viewCache.headTrackerToTrackerBase:"); + System.err.println(viewCache.headTrackerToTrackerBase); + + System.err.println("viewCache.headToHeadTracker:"); + System.err.println(viewCache.headToHeadTracker); + } + + if (viewCache.viewPolicy != View.HMD_VIEW) { + if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("screenViewCache.trackerBaseToImagePlate:"); + System.err.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.err.println("headTrackerToLeftImagePlate:"); + System.err.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.err.println("headToLeftImagePlate:"); + System.err.println(headToLeftImagePlate); + System.err.println("headToRightImagePlate:"); + System.err.println(headToRightImagePlate); + + } + } + + /** + * Routine to cache the current eye position in image plate + * coordinates. + */ + private void cacheEyePosition() { + if (viewCache.compatibilityModeEnable) { + // XXXX: 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.err.println("leftEyeInImagePlate = " + + leftEyeInImagePlate); + System.err.println("rightEyeInImagePlate = " + + rightEyeInImagePlate); + System.err.println("centerEyeInImagePlate = " + + centerEyeInImagePlate); + System.err.println("nominalEyeOffset = " + + nominalEyeOffset); + System.err.println(); + } + } + + private void computePlateToVworld() { + if (viewCache.compatibilityModeEnable) { + // XXXX: implement this correctly for compat mode + leftPlateToVworld.setIdentity(); + vworldToLeftPlate.setIdentity(); + } + else { + try { + leftPlateToVpc.invert(vpcToLeftPlate); + } + catch (SingularMatrixException e) { + leftPlateToVpc.setIdentity(); + /* + System.err.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.err.println("SingularMatrixException encountered when doing" + + " rightPlateToVpc invert"); + */ + } + + rightPlateToVworld.mul(vpcToVworld, rightPlateToVpc); + vworldToRightPlate.mul(vpcToRightPlate, vworldToVpc); + + } + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("vpcToVworld:"); + System.err.println(vpcToVworld); + System.err.println("vpcToLeftPlate:"); + System.err.println(vpcToLeftPlate); + if(useStereo) { + System.err.println("vpcToRightPlate:"); + System.err.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) { + // XXXX: implement this correctly for compat mode + headToVworld.setIdentity(); + } + else { + headToVworld.mul(leftPlateToVworld, headToLeftImagePlate); + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("leftPlateToVworld:"); + System.err.println(leftPlateToVworld); + System.err.println("headToLeftImagePlate:"); + System.err.println(headToLeftImagePlate); + System.err.println("...gives -> headToVworld:"); + System.err.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); + + // XXXX: 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.err.println("vpcToCoexistence:"); + System.err.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.err.println("coexistenceCenter = " + coexistenceCenter); + } + } + } + + private void computeCoexistenceToPlate() { + if (viewCache.compatibilityModeEnable) { + // XXXX: 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.err.println("coexistenceToLeftPlate:"); + System.err.println(coexistenceToLeftPlate); + if(useStereo) { + System.err.println("coexistenceToRightPlate:"); + System.err.println(coexistenceToRightPlate); + + } + } + } + + /** + * Computes the viewing matrices. + * + * computeView computes the following: + * + *
    + * left (& right) eye viewing matrices (only left is valid for mono view) + *
+ * + * This call works for both fixed screen and HMD displays. + */ + private void computeView(boolean doInfinite) { + 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.err.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.err.println("Left projection and view matrices"); + System.err.println("ecToCc (leftProjection) :"); + System.err.println(leftProjection); + System.err.println("vpcToEc:"); + System.err.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.err.println("Right projection and view matrices"); + System.err.println("ecToCc:"); + System.err.println("vpcToEc:"); + System.err.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; + } + + // XXXX: 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.err.println("Left projection and view matrices"); + System.err.println("Fl " + Fl + " B " + B); + System.err.println("leftEyeInImagePlate\n" + leftEyeInImagePlate); + System.err.println("Before : leftProjection\n" + leftProjection); + System.err.println("Before leftVpcToEc\n" + leftVpcToEc); + } + + buildProjView(leftEyeInImagePlate, coexistenceToLeftPlate, + vpcToLeftPlate, Fl, B, leftProjection, leftVpcToEc, false); + + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("After : leftProjection\n" + leftProjection); + System.err.println("After leftVpcToEc\n" + leftVpcToEc); + } + + computeFrustumPlanes(leftProjection, leftVpcToEc, + leftFrustumPlanes, leftFrustumPoints, + leftCcToVworld); + + if(useStereo) { + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) + System.err.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); + + } + } + } + // XXXX: 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.err.println("PARALLEL_PROJECTION ="); + else + System.err.println("PERSPECTIVE_PROJECTION ="); + + System.err.println(p); + + double projectionPlaneZ = ((p.mat[0] * xMax + p.mat[3] - p.mat[15]) / + (p.mat[14] - p.mat[2])); + + System.err.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(); + + + // XXXX: 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. + // + /* XXXX: + 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.err.println("ep = " + ep); + System.err.println("Lx = " + Lx + ", Hx = " + Hx); + System.err.println("Ly = " + Ly + ", Hy = " + Hy); + System.err.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.err.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.err.println("vpcToEc:"); + System.err.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.err.println("ccToVworld = " + ccToVworld); + try { + ccToVworld.invert(); + } + catch (SingularMatrixException e) { + ccToVworld.setIdentity(); + // System.err.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.err.println("\nvworldToCc = " + t); + System.err.println("ccToVworld = " + ccToVworld); + t.mul(ccToVworld); + System.err.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.err.println("left plane = " + frustumPlanes[0]); + //System.err.println("right plane = " + frustumPlanes[1]); + //System.err.println("top plane = " + frustumPlanes[2]); + //System.err.println("bottom plane = " + frustumPlanes[3]); + //System.err.println("front plane = " + frustumPlanes[4]); + //System.err.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() { + // XXXX: Document -- This will return the transform of left plate. + return leftPlateToVworld; + } + + + + Transform3D getLastVworldToImagePlate() { + // XXXX: Document -- This will return the transform of left plate. + return lastVworldToLeftPlate; + + } + + Transform3D getVworldToImagePlate() { + // XXXX: 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.err.println("eye dist = " + (centerEyeInImagePlate.z)); + //System.err.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.err.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.err.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.err.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/javax/media/j3d/CanvasViewEventCatcher.java b/src/javax/media/j3d/CanvasViewEventCatcher.java new file mode 100644 index 0000000..aadbf5f --- /dev/null +++ b/src/javax/media/j3d/CanvasViewEventCatcher.java @@ -0,0 +1,99 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.IllegalComponentStateException; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +/** + * 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 + private Canvas3D canvas; + private static final boolean DEBUG = false; + + CanvasViewEventCatcher(Canvas3D c) { + canvas = c; + } + + @Override + public void componentResized(ComponentEvent e) { + if (DEBUG) { + System.err.println("Component resized " + e); + } + + if(e.getComponent() == canvas ) { + if (DEBUG) { + System.err.println("It is canvas!"); + } + synchronized(canvas) { + synchronized (canvas.dirtyMaskLock) { + canvas.cvDirtyMask[0] |= Canvas3D.MOVED_OR_RESIZED_DIRTY; + canvas.cvDirtyMask[1] |= Canvas3D.MOVED_OR_RESIZED_DIRTY; + } + canvas.resizeGraphics2D = true; + } + + // see comment below + try { + canvas.newSize = canvas.getSize(); + canvas.newPosition = canvas.getLocationOnScreen(); + } catch (IllegalComponentStateException ex) {} + + } + } + + @Override + public void componentMoved(ComponentEvent e) { + if (DEBUG) { + System.err.println("Component moved " + e); + } + + synchronized(canvas) { + synchronized (canvas.dirtyMaskLock) { + canvas.cvDirtyMask[0] |= Canvas3D.MOVED_OR_RESIZED_DIRTY; + canvas.cvDirtyMask[1] |= 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/javax/media/j3d/CapabilityBits.java b/src/javax/media/j3d/CapabilityBits.java new file mode 100644 index 0000000..d601ff7 --- /dev/null +++ b/src/javax/media/j3d/CapabilityBits.java @@ -0,0 +1,541 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + private static final int NODE_UNUSED_BIT = 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; + + // Additional Node bits (must go after all existing Node subclass bits) + static final int NODE_ALLOW_PARENT_READ = 46; + static final int NODE_ALLOW_LOCALE_READ = 47; + + + // 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; + + // ShaderAppearance extends Appearance + static final int SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_READ = 22; + static final int SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_WRITE = 23; + static final int SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_READ = 24; + static final int SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_WRITE = 25; + + // 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; + static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_READ = 12; + static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_WRITE = 13; + static final int RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_READ = 14; + static final int RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_WRITE = 15; + + // 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; + + // ShaderProgram extends NodeComponent + static final int SHADER_PROGRAM_ALLOW_SHADERS_READ = 0; + static final int SHADER_PROGRAM_ALLOW_NAMES_READ = 1; + + // ShaderAttributeSet extends NodeComponent + static final int SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_READ = 0; + static final int SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_WRITE = 1; + + // ShaderAttribute extends NodeComponent + + // ShaderAttributeObject extends ShaderAttribute + static final int SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_READ = 0; + static final int SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_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; + static final int GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_READ = 22; + static final int GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_WRITE = 23; + + // Additional GeometryArray bits (must go after IndexedGeometryArray bits) + static final int INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_READ = 24; + static final int INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_WRITE = 25; + + // 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/javax/media/j3d/CapabilityNotSetException.java b/src/javax/media/j3d/CapabilityNotSetException.java new file mode 100644 index 0000000..f3f15d5 --- /dev/null +++ b/src/javax/media/j3d/CapabilityNotSetException.java @@ -0,0 +1,51 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/Clip.java b/src/javax/media/j3d/Clip.java new file mode 100644 index 0000000..427fb13 --- /dev/null +++ b/src/javax/media/j3d/Clip.java @@ -0,0 +1,313 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_APPLICATION_BOUNDS_READ, + ALLOW_BACK_DISTANCE_READ + }; + + /** + * Constructs a Clip node with default parameters. The default + * values are as follows: + *
    + * back clip distance : 100 meters + * application bounds : null
    + * application bounding leaf : null
    + *
+ */ + public Clip () { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs a Clip node with the specified back clip distance. + */ + public Clip(double backDistance) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * + * View.setFrontClipDistance. + * @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. + */ + @Override + void createRetained() { + this.retained = new ClipRetained(); + this.retained.setSource(this); + } + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/ClipRetained.java b/src/javax/media/j3d/ClipRetained.java new file mode 100644 index 0000000..78c4ae6 --- /dev/null +++ b/src/javax/media/j3d/ClipRetained.java @@ -0,0 +1,400 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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((Bounds)null); + } + + /** + * 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 + */ + @Override + 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 = 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 + */ + @Override + 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 = 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 = 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 + */ + + @Override + 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 = new J3dMessage(); + 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); + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + if (applicationRegion != null) { + applicationRegion.transform(xform.transform); + } + } + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + leafList.add(this); + } +} diff --git a/src/javax/media/j3d/ColorInterpolator.java b/src/javax/media/j3d/ColorInterpolator.java new file mode 100644 index 0000000..1377d7c --- /dev/null +++ b/src/javax/media/j3d/ColorInterpolator.java @@ -0,0 +1,314 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Enumeration; + +import javax.vecmath.Color3f; + +/** + * 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 not 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 = 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + ColorInterpolator ci = new ColorInterpolator(); + ci.duplicateNode(this, forceDuplicate); + return ci; + } + + + /** + * Copies all ColorInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/ColoringAttributes.java b/src/javax/media/j3d/ColoringAttributes.java new file mode 100644 index 0000000..5596154 --- /dev/null +++ b/src/javax/media/j3d/ColoringAttributes.java @@ -0,0 +1,370 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Color3f; + +/** + * The ColoringAttributes object defines attributes used in + * color selection and shading model. + * + *

+ * Color + *

+ * The setColor 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. + *

+ * There are two variations on the setColor 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. + *

+ * Shading Model + *

+ * The setShadeModel method sets the shade model for this + * ColoringAttributes component object. The shade model may be one of + * the following:

+ *

    + *
  • 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.
  • + *

    + *

  • 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.
  • + *

    + *

  • 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.
  • + *

    + *

  • 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.
  • + *

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_COLOR_READ, + ALLOW_SHADE_MODEL_READ + }; + + /** + * Constructs a ColoringAttributes node with default parameters. + * The default values are as follows: + *
    + * color = white (1,1,1)
    + * shade model = SHADE_GOURAUD
    + *
+ */ + public ColoringAttributes() { + // Just use default attributes + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + void createRetained() { + this.retained = new ColoringAttributesRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + + @Override + public NodeComponent cloneNodeComponent() { + ColoringAttributes ca = new ColoringAttributes(); + ca.duplicateNodeComponent(this); + return ca; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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. + */ + @Override + public String toString() { + StringBuffer str = new StringBuffer(getNamePrefix()); + str.append("javax.media.j3d.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/javax/media/j3d/ColoringAttributesRetained.java b/src/javax/media/j3d/ColoringAttributesRetained.java new file mode 100644 index 0000000..f419b1e --- /dev/null +++ b/src/javax/media/j3d/ColoringAttributesRetained.java @@ -0,0 +1,265 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3f; + +/** + * 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 + */ + @Override + 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); + } + } + + void updateNative(Context ctx, + float dRed, float dGreen, float dBlue, + float alpha, boolean lEnable) { + Pipeline.getPipeline().updateColoringAttributes(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 + */ + @Override + synchronized void initMirrorObject() { + ((ColoringAttributesRetained)mirror).set(this); + } + + /** Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + } + @Override + void handleFrequencyChange(int bit) { + if (bit == ColoringAttributes.ALLOW_COLOR_WRITE || + bit == ColoringAttributes.ALLOW_SHADE_MODEL_WRITE) { + setFrequencyChangeMask(bit, 0x1); + } + } + +} diff --git a/src/javax/media/j3d/CompileState.java b/src/javax/media/j3d/CompileState.java new file mode 100644 index 0000000..fdf777e --- /dev/null +++ b/src/javax/media/j3d/CompileState.java @@ -0,0 +1,310 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Vector; + +/** + * 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: +private final HashMap knownAppearances; + int numShapes = 0; + +// Shape merging stuff: +private final HashMap> shapeLists; + int numMergeSets = 0; + int numMergeShapes = 0; + + 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; + + 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() { + knownAppearances = new HashMap(); + shapeLists = new HashMap>(); +} + + // 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) { + // see if the appearance has allready been classified + if (app.map == this && app.mapAppearance != null) { + return app.mapAppearance; + } + + // check if this appearance equals one one in the Map + AppearanceRetained retval = knownAppearances.get(app); + if (retval == null) { + // not found, put this appearance in the map + knownAppearances.put(app, app); + retval = app; + } + + // cache this result on the appearance in case it appears again + app.map = this; + app.mapAppearance = retval; + + return retval; +} + + void addShape(Shape3DRetained shape) { + if (parentGroup != null) { + // sort the shapes into lists with equivalent appearances + Vector list = shapeLists.get(shape.appearance); + if (list == 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 = 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.err.println("numTransformGroups= " + numTransformGroups); + System.err.println("numStaticTransformGroups= " + numStaticTransformGroups); + System.err.println("numMergedTransformGroups= " + numMergedTransformGroups); + System.err.println("numGroups= " + numGroups); + System.err.println("numMergedGroups= " + numMergedGroups); + System.err.println("numShapes= " + numShapes); + System.err.println("numShapesWStaticTG= " + numShapesWStaticTG); + System.err.println("numMergeShapes= " + numMergeShapes); + System.err.println("numMergeSets= " + numMergeSets); + System.err.println("numLinks= " + numLinks); + System.err.println("numSwitches= " + numSwitches); + System.err.println("numOrderedGroups= " + numOrderedGroups); + System.err.println("numMorphs= " + numMorphs); + } + + void doShapeMerge() { + + // System.err.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 = 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 = 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 = 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.err.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.err.println("Dest is "+ parentGroup); + System.err.println("Compile Shape "+mergeShape); + System.err.println(mergeShape.geometryList.size()+" geoemtryList"); + for (int j = 0; j < mergeShape.geometryList.size(); j++) { + GeometryRetained geo = mergeShape.geometryList.get(j); + if (geo != null) + System.err.println("\t Geo_type = " + geo.geoType); + } + + System.err.println(numMerge+" Shapes were merged "); + for (int j = 0; j < numMerge; j++) { + System.err.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/javax/media/j3d/CompressedGeometry.java b/src/javax/media/j3d/CompressedGeometry.java new file mode 100644 index 0000000..a595f1c --- /dev/null +++ b/src/javax/media/j3d/CompressedGeometry.java @@ -0,0 +1,428 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The compressed geometry object is used to store geometry in a + * compressed format. Using compressed geometry may increase the speed + * objects can be sent over the network. Note that the geometry will + * be decompressed in memory, so the application will not see any + * memory savings. + *

+ * 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. + *

+ *

    + *
  • + * By Copying: + * 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. + *
  • + *
  • By Reference: + * 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 byReference flag + * set to true. 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. + *
  • + *
+ * + * @deprecated As of Java 3D version 1.4. + */ +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; + + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_COUNT_READ, + ALLOW_HEADER_READ, + ALLOW_GEOMETRY_READ, + ALLOW_REF_DATA_READ + }; + + /** + * Package scoped default constructor for use by cloneNodeComponent. + */ + CompressedGeometry() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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 Java 3D API Specification. + * + * @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 Java 3D API Specification. + * + * @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")) ; + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + // 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 ; + } + + /** + * This constructor is not implemented. + * + * @exception UnsupportedOperationException this constructor is not + * implemented + * + * @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. + // + return decompressor.toTriangleStripArrays(cgr) ; + } + + + /** + * Retrieves the data access mode for this CompressedGeometry object. + * + * @return true if the data access mode for this + * CompressedGeometry object is by-reference; + * false 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. + * + * @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, which is + * always null since NIO buffers are not supported for + * CompressedGeometry objects. + * + * @return 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 J3DBuffer getCompressedGeometryBuffer() { + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_REF_DATA_READ)) + throw new CapabilityNotSetException + (J3dI18N.getString("CompressedGeometry6")) ; + + return null; + } + + + /** + * Creates the retained mode CompressedGeometryRetained object that this + * CompressedGeometry object will point to. + */ + @Override + void createRetained() { + this.retained = new CompressedGeometryRetained() ; + this.retained.setSource(this) ; + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + 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/javax/media/j3d/CompressedGeometryHeader.java b/src/javax/media/j3d/CompressedGeometryHeader.java new file mode 100644 index 0000000..33b1742 --- /dev/null +++ b/src/javax/media/j3d/CompressedGeometryHeader.java @@ -0,0 +1,256 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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. + *

+ * All instance data is declared public and no get or set methods are + * provided. + * + * @see CompressedGeometry + * + * @deprecated As of Java 3D version 1.4. + */ +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.

+ * + * 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.

+ * + * If the CompressedGeometry is created with copy access semantics, then + * size bytes of compressed geometry data are copied from the + * offset indicated by start 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 start field set to 0. + */ + public int start ; + + /** + * A point that defines the lower bound of the x, + * y, and z 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 x, + * y, and z 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 + */ + @Override + 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/javax/media/j3d/CompressedGeometryRenderMethod.java b/src/javax/media/j3d/CompressedGeometryRenderMethod.java new file mode 100644 index 0000000..6633b57 --- /dev/null +++ b/src/javax/media/j3d/CompressedGeometryRenderMethod.java @@ -0,0 +1,112 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + public boolean render(RenderMolecule rm, Canvas3D cv, + RenderAtomListInfo ra, int dirtyBits) { + if (rm.doInfinite) { + cv.updateState(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(dirtyBits); + renderCompressedGeo(ra, rm, cv); + isVisible = true; + } + } + else { + if (!VirtualUniverse.mc.viewFrustumCulling || + ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(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, + cv.screen.screen, + rm.textureBin.attributeBin.ignoreVertexColors); + } +} diff --git a/src/javax/media/j3d/CompressedGeometryRetained.java b/src/javax/media/j3d/CompressedGeometryRetained.java new file mode 100644 index 0000000..aba0277 --- /dev/null +++ b/src/javax/media/j3d/CompressedGeometryRetained.java @@ -0,0 +1,470 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d ; +import javax.vecmath.Point3d; + +/** + * 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 ; + + /** + * Formerly native method that returns availability of a native by-reference + * rendering API for compressed geometry. + */ + private boolean decompressByRef(Context ctx) { + return false; + } + + /** + * Formerly native method that returns availability of hardware + * rendering (and acceleration) for compressed geometry of the + * given version. + */ + private boolean decompressHW(Context ctx, int majorVersion, int minorVersion) { + return false; + } + + /** + * Formerly native method that does HW compressed geometry rendering + */ + private void execute(Context ctx, int version, int bufferType, + int bufferContents, int renderFlags, + int offset, int size, byte[] geometry) { + + assert false : "This method should never be called!"; + } + + /** + * Method for calling native execute() method on behalf of the J3D renderer. + */ + @Override + void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, + boolean updateAlpha, float alpha, + int screen, boolean ignoreVertexColors) { + + // XXXX: 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. + */ + @Override + void computeBoundingBox() { + } + + /** + * Update this object. Compressed geometry is immutable so there's + * nothing to do. + */ + @Override + 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) ; + + geoBounds.getCenter(this.centroid); + 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 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 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. + // + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + GeometryRetained geomR = getPickGeometry() ; + return (geomR != null ? + geomR.intersect(pickShape, pickInfo, flags, iPnt, geom, geomIndex) : false); + } + + @Override + boolean intersect(Bounds targetBound) { + GeometryRetained geom = getPickGeometry() ; + return (geom != null ? geom.intersect(targetBound) : false); + } + + @Override + boolean intersect(Transform3D thisToOtherVworld, GeometryRetained g) { + GeometryRetained geom = getPickGeometry() ; + return (geom != null ? + geom.intersect(thisToOtherVworld, g) : false); + } + + @Override + 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. + */ + @Override + 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 ; + } + } + + @Override + int getClassType() { + return COMPRESS_TYPE; + } +} diff --git a/src/javax/media/j3d/ConeSound.java b/src/javax/media/j3d/ConeSound.java new file mode 100644 index 0000000..b2f2629 --- /dev/null +++ b/src/javax/media/j3d/ConeSound.java @@ -0,0 +1,960 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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. + *

+ * Distance Gain attuation + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

+ * Direction Methods + *

    + * This value is the sound source's direction vector. It is the axis from + * which angular distance is measured. + *

+ * Angular Attenuation + *

    + * Besides sound (linear) distance attenuation a ConeSound can optionally + * define angular gain and filter attenuation. + *

    + * 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. + *

    + * 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). + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * The getAngularAttenuationArrayLength method returns the length of the angular + * attenuation arrays. Arrays passed into getAngularAttenuation methods + * should all be at least this size. + *

+ */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_DIRECTION_READ, + ALLOW_ANGULAR_ATTENUATION_READ + }; + + /** + * Constructs and initializes a new ConeSound node using default + * parameters. The following default values are used: + *
    + * Direction vector: (0.0, 0.0, 1.0)
    + * Angular attenuation: + * ((0.0, 1.0, Sound.NO_FILTER),(p/2, 0.0, Sound.NO_FILTER))
    + *
+ */ + public ConeSound() { + // Uses default values defined in ConeSoundRetained.java + super(); + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + *

+ * 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 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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + ConeSound c = new ConeSound(); + c.duplicateNode(this, forceDuplicate); + return c; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + * + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * ConeSound + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + + /** + * Copies all ConeSound information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/ConeSoundRetained.java b/src/javax/media/j3d/ConeSoundRetained.java new file mode 100644 index 0000000..514f172 --- /dev/null +++ b/src/javax/media/j3d/ConeSoundRetained.java @@ -0,0 +1,656 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point2f; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + + + +/** + * 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) + 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; icloneTree call an updated reference was requested + * for a node that did not get cloned. This happens when a sub-graph is + * duplicated via cloneTree 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. + *

+ * If dangling references are to be allowed during the cloneTree call, + * cloneTree should be called with the + * allowDanglingReferences parameter set to true. + * @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/javax/media/j3d/DecalGroup.java b/src/javax/media/j3d/DecalGroup.java new file mode 100644 index 0000000..a153903 --- /dev/null +++ b/src/javax/media/j3d/DecalGroup.java @@ -0,0 +1,94 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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). + *

+ * 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. + */ + @Override + void createRetained() { + this.retained = new DecalGroupRetained(); + this.retained.setSource(this); + } + + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + DecalGroup dg = new DecalGroup(); + dg.duplicateNode(this, forceDuplicate); + return dg; + } + +} diff --git a/src/javax/media/j3d/DecalGroupRetained.java b/src/javax/media/j3d/DecalGroupRetained.java new file mode 100644 index 0000000..3d0d4fa --- /dev/null +++ b/src/javax/media/j3d/DecalGroupRetained.java @@ -0,0 +1,34 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +class DecalGroupRetained extends OrderedGroupRetained { + + DecalGroupRetained() { + this.nodeType = NodeRetained.DECALGROUP; + } +} diff --git a/src/javax/media/j3d/DefaultRenderMethod.java b/src/javax/media/j3d/DefaultRenderMethod.java new file mode 100644 index 0000000..61a4ed0 --- /dev/null +++ b/src/javax/media/j3d/DefaultRenderMethod.java @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The RenderMethod interface is used to create various ways to render + * different geometries. + */ + +class DefaultRenderMethod implements RenderMethod { + /** + * The actual rendering code for this RenderMethod + */ + @Override + public boolean render(RenderMolecule rm, Canvas3D cv, + 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(dirtyBits); + ra.geometry().execute(cv, ra.renderAtom, + rm.isNonUniformScale, + rm.useAlpha, rm.alpha, + cv.screen.screen, + rm.textureBin.attributeBin. + ignoreVertexColors); + isVisible = true; + } + } + else { + if (!VirtualUniverse.mc.viewFrustumCulling || + ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.raIsVisible = true; + cv.updateState(dirtyBits); + ra.geometry().execute(cv, ra.renderAtom, rm.isNonUniformScale, + rm.useAlpha, rm.alpha, + cv.screen.screen, + rm.textureBin.attributeBin. + ignoreVertexColors); + isVisible = true; + } + else { + cv.raIsVisible = false; + } + cv.ra = ra.renderAtom; + } + ra = ra.next; + } + + return isVisible; + + } + + + +} diff --git a/src/javax/media/j3d/DepthComponent.java b/src/javax/media/j3d/DepthComponent.java new file mode 100644 index 0000000..02994fb --- /dev/null +++ b/src/javax/media/j3d/DepthComponent.java @@ -0,0 +1,89 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SIZE_READ, + ALLOW_DATA_READ + }; + + /** + * default constructor + */ + DepthComponent() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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/javax/media/j3d/DepthComponentFloat.java b/src/javax/media/j3d/DepthComponentFloat.java new file mode 100644 index 0000000..3a304c5 --- /dev/null +++ b/src/javax/media/j3d/DepthComponentFloat.java @@ -0,0 +1,134 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + void createRetained() { + this.retained = new DepthComponentFloatRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + 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 originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/DepthComponentFloatRetained.java b/src/javax/media/j3d/DepthComponentFloatRetained.java new file mode 100644 index 0000000..24290bd --- /dev/null +++ b/src/javax/media/j3d/DepthComponentFloatRetained.java @@ -0,0 +1,88 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/DepthComponentInt.java b/src/javax/media/j3d/DepthComponentInt.java new file mode 100644 index 0000000..34963ea --- /dev/null +++ b/src/javax/media/j3d/DepthComponentInt.java @@ -0,0 +1,132 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + void createRetained() { + this.retained = new DepthComponentIntRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + 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 originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/DepthComponentIntRetained.java b/src/javax/media/j3d/DepthComponentIntRetained.java new file mode 100644 index 0000000..a382da7 --- /dev/null +++ b/src/javax/media/j3d/DepthComponentIntRetained.java @@ -0,0 +1,88 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/DepthComponentNative.java b/src/javax/media/j3d/DepthComponentNative.java new file mode 100644 index 0000000..070d00c --- /dev/null +++ b/src/javax/media/j3d/DepthComponentNative.java @@ -0,0 +1,124 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + void createRetained() { + this.retained = new DepthComponentNativeRetained(); + this.retained.setSource(this); + } + + /** + * Creates a new DepthComponentNative object. Called from a Leaf node's + * duplicateNode method. + * + * @return a duplicate of the DepthComponentNative object. + * + * @see Node#duplicateNode + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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 originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/DepthComponentNativeRetained.java b/src/javax/media/j3d/DepthComponentNativeRetained.java new file mode 100644 index 0000000..220a328 --- /dev/null +++ b/src/javax/media/j3d/DepthComponentNativeRetained.java @@ -0,0 +1,77 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/DepthComponentRetained.java b/src/javax/media/j3d/DepthComponentRetained.java new file mode 100644 index 0000000..d28ab87 --- /dev/null +++ b/src/javax/media/j3d/DepthComponentRetained.java @@ -0,0 +1,62 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/DirectionalLight.java b/src/javax/media/j3d/DirectionalLight.java new file mode 100644 index 0000000..69b4a46 --- /dev/null +++ b/src/javax/media/j3d/DirectionalLight.java @@ -0,0 +1,220 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_DIRECTION_READ + }; + + /** + * Constructs a DirectionalLight node with default parameters. + * The default values are as follows: + *

    + * direction : (0,0,-1)
    + *
+ */ + public DirectionalLight() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((DirectionalLightRetained)this.retained).initDirection(direction); + } + + /** + * Creates the retained mode DirectionalLightRetained object that this + * DirectionalLight component object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + DirectionalLight d = new DirectionalLight(); + d.duplicateNode(this, forceDuplicate); + return d; + } + + + /** + * Copies all DirectionalLight information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/DirectionalLightRetained.java b/src/javax/media/j3d/DirectionalLightRetained.java new file mode 100644 index 0000000..325762b --- /dev/null +++ b/src/javax/media/j3d/DirectionalLightRetained.java @@ -0,0 +1,218 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Vector3f; + +/** + * 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((Bounds)null); + } + + /** + * 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 = new J3dMessage(); + 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); + } + } + + + + @Override + 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. + */ + @Override + // 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(); + 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); + } + + + @Override + void update(Context ctx, int lightSlot, double scale) { + Pipeline.getPipeline().updateDirectionalLight(ctx, + lightSlot, color.x, color.y, color.z, + xformDirection.x, xformDirection.y, + xformDirection.z); + } + + // Clones only the retained side, internal use only + @Override + 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 + @Override + void updateTransformChange() { + super.updateTransformChange(); + + getLastLocalToVworld().transform(direction, xformDirection); + xformDirection.normalize(); + + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + xform.transform.transform(direction, direction); + } +} diff --git a/src/javax/media/j3d/DisplayListRenderMethod.java b/src/javax/media/j3d/DisplayListRenderMethod.java new file mode 100644 index 0000000..a5c3233 --- /dev/null +++ b/src/javax/media/j3d/DisplayListRenderMethod.java @@ -0,0 +1,269 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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]; + + /** + * The actual rendering code for this RenderMethod + */ + @Override + public boolean render(RenderMolecule rm, Canvas3D cv, + RenderAtomListInfo ra, + int dirtyBits) { + + if (rm.doInfinite || + !VirtualUniverse.mc.viewFrustumCulling || + rm.vwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(dirtyBits); + cv.callDisplayList(cv.ctx, rm.displayListId, + rm.isNonUniformScale); + return true; + } + return false; + } + + public boolean renderSeparateDlists(RenderMolecule rm, + Canvas3D cv, + RenderAtomListInfo r, int dirtyBits) { + + if (rm.doInfinite) { + cv.updateState(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(dirtyBits); + cv.callDisplayList(cv.ctx, + ((GeometryArrayRetained)r.geometry()).dlistId, + rm.isNonUniformScale); + isVisible = true; + } + } + else { + if (r.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(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, + RenderAtomListInfo r, + int dirtyBits) { + + if (rm.doInfinite) { + cv.updateState(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(dirtyBits); + cv.callDisplayList(cv.ctx, r.renderAtom.dlistIds[r.index], + rm.isNonUniformScale); + isVisible = true; + } + } + else { + if (r.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(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.maxTexCoordSets)) { + + 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, + Context ctx) { + GeometryArrayRetained geo; + + geo = (GeometryArrayRetained)ra.geometry(); + if ((geo.texCoordSetMap != null) && + (geo.texCoordSetMap.length > cv.maxTexCoordSets)) { + 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.maxTexCoordSets)) { + + 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/javax/media/j3d/DistanceLOD.java b/src/javax/media/j3d/DistanceLOD.java new file mode 100644 index 0000000..244c06a --- /dev/null +++ b/src/javax/media/j3d/DistanceLOD.java @@ -0,0 +1,310 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Enumeration; + +import javax.vecmath.Point3f; + +/** + * 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 n monotonically increasing distance values is + * specified, such that distances[0] is associated with the highest level of + * detail and distances[n-1] is associated with the lowest level of + * detail. Based on the actual distance from the viewer to + * this DistanceLOD node, these n + * distance values [0, n-1] select from among n+1 + * levels of detail [0, n]. If d 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: + *

+ *

    + * 0, if d <= distances[0] + *
    + * i, if distances[i-1] < d <= distances[i] + *
    + * n, if d > distances[n-1] + *
+ *

+ * 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[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); + } + } + // Insert wakeup condition into queue + wakeupOn(wakeupFrame); + + } + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + DistanceLOD d = new DistanceLOD(); + d.duplicateNode(this, forceDuplicate); + return d; + } + + + /** + * Copies all DistanceLOD information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/Drawable.java b/src/javax/media/j3d/Drawable.java new file mode 100644 index 0000000..833b6cd --- /dev/null +++ b/src/javax/media/j3d/Drawable.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Tagging interface for drawable (window) objects. The rendering pipelines + * will define concrete classes that implement this interface. All code that + * uses the tagged objects will be in the pipelines. + */ +interface Drawable { + // No methods or constants defined at this time +} diff --git a/src/javax/media/j3d/DrawingSurfaceObject.java b/src/javax/media/j3d/DrawingSurfaceObject.java new file mode 100644 index 0000000..3d2c591 --- /dev/null +++ b/src/javax/media/j3d/DrawingSurfaceObject.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/EnvironmentSet.java b/src/javax/media/j3d/EnvironmentSet.java new file mode 100644 index 0000000..ab22747 --- /dev/null +++ b/src/javax/media/j3d/EnvironmentSet.java @@ -0,0 +1,549 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3f; + +/** + * 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); + } + + private 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 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; + + this.modelClip = modelClip; + enableMCMaskCache = 0; + if (modelClip != null) { + 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; + + // Issue 466 : add the env set to the light, fog, and model clip + // lists only after the newly constructed env set is initialized + if (lightList != null) { + for (i=0; i 0) { + a = 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 = 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) { + 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; + + 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++) { + lights.get(i).environmentSets.remove(this); + } + sz = ambLights.size(); + for (i = 0; i < sz; i++) { + 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 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) { + double scale; + boolean updateSceneAmbient = false, updateLightEnables = false; + boolean updateModelClip = false, updateFog = false; + // within frame + if (cv.environmentSet != this ) { + if (cv.enableMask != enableMask) { + updateLightEnables = true; + } + + if (cv.sceneAmbient.x != sceneAmbient.x || + cv.sceneAmbient.y != sceneAmbient.y || + cv.sceneAmbient.z != sceneAmbient.z ) { + updateSceneAmbient = true; + } + + if (cv.fog != fog) { + updateFog = true; + } + + if (cv.modelClip != modelClip) { + updateModelClip = true; + } + } + + // Check for dirtybit. + if ((cv.canvasDirty & (Canvas3D.LIGHTENABLES_DIRTY| + Canvas3D.AMBIENTLIGHT_DIRTY| + Canvas3D.FOG_DIRTY| + Canvas3D.MODELCLIP_DIRTY| + Canvas3D.VIEW_MATRIX_DIRTY)) != 0) { + + if ((cv.canvasDirty & Canvas3D.LIGHTENABLES_DIRTY) != 0) { + updateLightEnables = true; + } + + if ((cv.canvasDirty & Canvas3D.AMBIENTLIGHT_DIRTY) != 0) { + updateSceneAmbient = true; + } + + if ((cv.canvasDirty & Canvas3D.FOG_DIRTY) != 0) { + updateFog = true; + } + + if ((cv.canvasDirty & Canvas3D.MODELCLIP_DIRTY) != 0) { + updateModelClip = true; + } + + if ((cv.canvasDirty & Canvas3D.VIEW_MATRIX_DIRTY) != 0) { + updateFog = true; + updateModelClip = true; + } + } + + // do states update here. + if (updateLightEnables) { + cv.setLightEnables(cv.ctx, enableMask, renderBin.maxLights); + cv.enableMask = enableMask; + } + + if (updateSceneAmbient) { + cv.setSceneAmbient(cv.ctx, sceneAmbient.x, + sceneAmbient.y, sceneAmbient.z); + cv.sceneAmbient.set(sceneAmbient); + } + + if (updateFog) { + 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 (updateModelClip) { + 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 | + Canvas3D.VIEW_MATRIX_DIRTY); + + cv.environmentSet = this; + + } +} diff --git a/src/javax/media/j3d/EventCatcher.java b/src/javax/media/j3d/EventCatcher.java new file mode 100644 index 0000000..d0e561f --- /dev/null +++ b/src/javax/media/j3d/EventCatcher.java @@ -0,0 +1,433 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + + +/** + * 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, MouseWheelListener, WindowListener { + + // The canvas associated with this event catcher + private Canvas3D canvas; + private static final boolean DEBUG = false; + private boolean stopped = false; + + /** + * flags for event listeners + */ + private boolean focusEvents = false; + private boolean keyEvents = false; + private boolean mouseEvents = false; + private boolean mouseMotionEvents = false; + private boolean mouseWheelEvents = false; + private boolean mouseListenerAdded = false; + + EventCatcher(Canvas3D c) { + canvas = c; + } + + 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; + } + } + + void enableMouseWheelEvents() { + if (!mouseWheelEvents) { + canvas.addMouseWheelListener(this); + mouseWheelEvents = true; + } + } + + + void disableMouseWheelEvents() { + if (mouseWheelEvents) { + canvas.removeMouseWheelListener(this); + mouseWheelEvents = false; + } + } + + + @Override + public void componentResized(ComponentEvent e) { + if (e.getSource() == canvas) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + canvas.evaluateVisiblilty(); + canvas.redraw(); + } + } + + @Override + public void componentMoved(ComponentEvent e) { + if (e.getSource() == canvas) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + + // Issue 458 - the following is not needed for a move +// if (VirtualUniverse.mc.isD3D()) { +// canvas.notifyD3DPeer(Canvas3D.RESIZE); +// } +// canvas.evaluateVisiblilty(true); + } + } + + @Override + public void componentHidden(ComponentEvent e) { + if (DEBUG) { + System.err.println(e); + } + if (e.getSource() == canvas) { + canvas.sendEventToBehaviorScheduler(e); + } + canvas.evaluateVisiblilty(); + } + + @Override + public void componentShown(ComponentEvent e) { + if (DEBUG) { + System.err.println(e); + } + if (e.getSource() == canvas) { + canvas.sendEventToBehaviorScheduler(e); + } + canvas.evaluateVisiblilty(); + } + + @Override + public void focusGained(FocusEvent e) { + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void focusLost(FocusEvent e) { + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void keyTyped(KeyEvent e) { + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void keyPressed(KeyEvent e) { + canvas.sendEventToBehaviorScheduler(e); + + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void keyReleased(KeyEvent e) { + canvas.sendEventToBehaviorScheduler(e); + if (stopped) { + stopped = false; + } else { + stopped = true; + } + if (DEBUG) { + System.err.println(e); + } + } + + @Override + 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.err.println(e); + } + } + + @Override + 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.err.println(e); + } + } + + @Override + public void mouseExited(MouseEvent e) { + if (mouseEvents) + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (mouseEvents) + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (mouseEvents) + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + // Note : We don't have to test for mouseMotionEvent here because + // this routine will never be called unless mouseMotionEvent is enabled. + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + // Note : We don't have to test for mouseMotionEvent here because + // this routine will never be called unless mouseMotionEvent is enabled. + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + // Note : We don't have to test for mouseWheelEvent here because + // this routine will never be called unless mouseWheelEvent is enabled. + canvas.sendEventToBehaviorScheduler(e); + if (DEBUG) { + System.err.println(e); + } + } + + /* + * WindowListener methods + */ + @Override + public void windowClosed(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + // Issue 458 - Don't set canvas visible to false + } + + @Override + public void windowClosing(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + // Issue 458 - Don't set canvas.visible to false + } + + @Override + public void windowActivated(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + } + + @Override + public void windowDeactivated(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + if (canvas.view != null) { + canvas.view.sendEventToSoundScheduler(e); + } + canvas.evaluateVisiblilty(); + } + + @Override + public void windowIconified(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + if (canvas.view != null) { + canvas.view.sendEventToSoundScheduler(e); + } + canvas.evaluateVisiblilty(); + } + + @Override + public void windowOpened(WindowEvent e) { + if (DEBUG) { + System.err.println(e); + } + canvas.sendEventToBehaviorScheduler(e); + canvas.evaluateVisiblilty(); + } + + void reset() { + focusEvents = false; + keyEvents = false; + mouseEvents = false; + mouseMotionEvents = false; + mouseWheelEvents = false; + mouseListenerAdded = false; + stopped = false; + } + +} + diff --git a/src/javax/media/j3d/ExceptionStrings.properties b/src/javax/media/j3d/ExceptionStrings.properties new file mode 100644 index 0000000..05ee875 --- /dev/null +++ b/src/javax/media/j3d/ExceptionStrings.properties @@ -0,0 +1,1003 @@ +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 +Background12=Background: Live Background with an ImageComponent2D that is being used by a Canvas3D as an off-screen buffer. +Background13=Background: In Immediate mode context with an ImageComponent2D that is being used by a Canvas3D as an off-screen buffer. +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=WARNING: Canvas3D constructed with 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 has to be a 3-component or 4-component format. +Canvas3D17=Canvas3D: GraphicsConfiguration is not compatible with Canvas3D +Canvas3D18=will attempt to use a default GraphicsConfiguration +Canvas3D19=Canvas3D: null GraphicsConfiguration +Canvas3D20=Canvas3D does not support serialization +Canvas3D23=Unable to get FBConfig from GraphicsConfiguration +Canvas3D24=Canvas3D: Can't force render of offscreen buffer because it is an automatic one. +Canvas3D25=Canvas3D: AutoOffscreenCanvas3D must be used as off-screen Canvas3D. +Canvas3D26=Canvas3D: ImageComponent2D is already part of a live scene graph. +Canvas3D27=Canvas3D: ImageComponent2D is already being used by an immediate mode context. +Canvas3D28=Canvas3D: ImageComponent2D is already being used by by another Canvas3D as an off-screen buffer. +Canvas3D29=Canvas3D: Non-recoverable graphics configuration error +Canvas3D30=Canvas3D: Non-recoverable off-screen rendering error +Canvas3D31=Canvas3D: Can't wait for off-screen rendering in a canvas callback +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 +BranchGroup4=BranchGroup: Mode has to be either PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY +BranchGroup5=BranchGroup: Mode can't be PickInfo.PICK_GEOMETRY if PickShape is PickPoint +BranchGroup6=BranchGroup: CLOSEST_GEOM_INFO and ALL_GEOM_INFO can't be set together. +BranchGroup7=BranchGroup: Mode can't be PICK_BOUNDS if geometry information is needed +BranchGroup8=BranchGroup: PickShape can't be PickBounds if geometry information is needed +BranchGroup9=BranchGroup: Cannot call picking under a SharedGroup node +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 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 texture coordinates +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 +GeometryArray124=GeometryArray: texCoordSetCount < 0 +GeometryArray125=GeometryArray: vertexAttrCount < 0 +GeometryArray126=GeometryArray: no capability to set vertex attributes +GeometryArray127=GeometryArray: no capability to read vertex attributes +GeometryArray128=GeometryArray: VERTEX_ATTRIBUTES flag must not be set in INTERLEAVED mode +GeometryArray129=GeometryArray: vertex attr array length is incorrect +GeometryArray130=GeometryArray: initial vertex attr index + valid vertex count > vertex count +GeometryArray131=GeometryArray: vertexAttrCount > 0, but VERTEX_ATTRIBUTES flag is not set +GeometryArray132=GeometryArray: vertexAttrCount != vertexAttrSizes.length +GeometryArray133=GeometryArray: vertexAttrSize value out of range +GeometryArray134=GeometryArray: vertexAttrSize invalid for this method +GeometryArray135=GeometryArray: USE_COORD_INDEX_ONLY bit cannot be set for non-indexed geometry +GeometryArray136=GeometryArray: BY_REFERENCE_INDICES bit can be set only for indexed geometry +GeometryArray137=GeometryArray: BY_REFERENCE_INDICES bit can be set only if BY_REFERENCE bit is also set +GeometryArray138=GeometryArray: BY_REFERENCE_INDICES bit can be set only if USE_COORD_INDEX_ONLY bit is also set +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 +GeometryStripArrayRetained0=Illegal stripVertexCounts +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 +GeometryStripArray8=GeometryStripArray: initial vertex attr 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 +GraphicsContext3D29=GraphicsContext3D: For readRaster, byReference image should be an instance of BufferedImage +GraphicsContext3D30=GraphicsContext3D: Appearance has an ImageComponent2D used by an OffScreen Canvas3D. +GraphicsContext3D31=GraphicsContext3D: Background's ImageComponent2D is already being used as an OffScreen Canvas3D. +GraphicsContext3D32=GraphicsContext3D: Raster's ImageComponent2D is already being used as an OffScreen Canvas3D. +GraphicsContext3D33=GraphicsContext3D: Raster's ImageComponent2D is not of ImageClass.BUFFERED_IMAGE. +GraphicsContext3D34=GraphicsContext3D: Raster's ImageComponent2D is in by-reference mode and its RenderedImage is null. +GraphicsContext3D35=GraphicsContext3D: Raster's ImageComponent2D format is not a 3-component format or a 4-component format. +GraphicsContext3D36=GraphicsContext3D: Raster and Raster's ImageComponent2D are part of a live scene graph. +GraphicsContext3D37=GraphicsContext3D: Raster's ImageComponent2D is being used by an immediate mode context, or by a Canvas3D as an off-screen buffer. +ImageComponent0=ImageComponent: no capability to get width +ImageComponent1=ImageComponent: no capability to get height +ImageComponent2=ImageComponent: no capability to get format +ImageComponent3=ImageComponent: This image is being used by a Canvas3D as an off-screen buffer. +ImageComponent4=ImageComponent: The image class of this object is ImageClass.NIO_IMAGE_BUFFER. +ImageComponent5=ImageComponent: Mismatch in the number of components between format and image buffer. +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 +ImageComponent2D6=ImageComponent2D: type mismatch between sub-image and existing image. +ImageComponent2D7=ImageComponent2D: Nio buffer image must be byReference is true. +ImageComponent2D8=ImageComponent2D: Nio buffer image must be yUp is true. +ImageComponent2D9=ImageComponent2D: The image class is not ImageClass.NIO_IMAGE_BUFFER. +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 +Locale5=Locale: Mode has to be either PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY +Locale6=Locale: Mode can't be PickInfo.PICK_GEOMETRY if PickShape is PickPoint +Locale7=Locale: CLOSEST_GEOM_INFO and ALL_GEOM_INFO can't be set together. +Locale8=Locale: Mode can't be PICK_BOUNDS if geometry information is needed +Locale9=Locale: PickShape can't be PickBounds if geometry information is needed +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 +IndexedGeometryArray28=IndexedGeometryArray: no capability to set vertex attribute index +IndexedGeometryArray29=IndexedGeometryArray: no capability to get vertex attribute index +IndexedGeometryArray30=IndexedGeometryArray: index vertexAttr value greater than the array length +IndexedGeometryArray31=IndexedGeometryArray: cannot access indices directly in BY_REFERENCE_INDICES mode +IndexedGeometryArray32=IndexedGeometryArray: can access indices by reference only in BY_REFERENCE_INDICES mode +IndexedGeometryArray33=IndexedGeometryArray: coordIndices array length < initial index index + valid index count +IndexedLineArray0=IndexedLineArray: illegal vertexCount +IndexedLineArray1=IndexedLineArray: illegal indexCount +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 +MorphRetained9=Morph: vertex attributes are not supported +NioImageBuffer0=NioImageBuffer: image width is not positive +NioImageBuffer1=NioImageBuffer: image height is not positive +NioImageBuffer2=NioImageBuffer: unsupported image type +NioImageBuffer3=NioImageBuffer: NIO buffer limit is incorrect +NioImageBuffer4=NioImageBuffer: NIO buffer type does not match image type +NioImageBuffer5=NioImageBuffer: NIO buffer does not match native byte order of underlying platform +Node0=Node: no capability to read parent +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 compiled but not live +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 +Node17=Node: no capability to read locale +PickInfo0=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_GEOMETRY_READ +PickInfo1=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_INTERSECT +PickInfo2=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_COORDINATE_READ +PickInfo3=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_COUNT_READ +PickInfo4=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_FORMAT_READ +PickInfo5=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_COORDINATE_INDEX_READ +PickInfo6=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_GEOMETRY_ARRAY_READ +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 +NodeComponent2=Live NodeComponent with an ImageComponent2D that is being used by a Canvas3D as an off-screen buffer. +NodeComponent3=In Immediate mode context with an ImageComponent2D that is being used by a Canvas3D as an off-screen buffer. +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 +Raster12=Raster: Live Raster with an ImageComponent2D that is being used by a Canvas3D as an off-screen buffer. +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 +Renderer0=Renderer: Unexpected RuntimeException caught during buffer swap +Renderer1=Renderer: Error initializing GraphicsConfiguration properties +Renderer2=Renderer: Error creating Canvas3D graphics context for queryProperties() +Renderer3=Renderer: Error in GraphicsConfigTemplate3D.getBestConfiguration() +Renderer4=Renderer: Error in GraphicsConfigTemplate3D.isConfigSupported() +Renderer5=Renderer: Error creating Canvas3D off-screen buffer +Renderer6=Renderer: Error creating immediate mode Canvas3D graphics context +Renderer7=Renderer: Error creating Canvas3D graphics context +Renderer8=Renderer: Unexpected RuntimeException caught during rendering +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 +RenderingAttributes14=RenderingAttributes: no capability to set depth test function +RenderingAttributes15=RenderingAttributes: no capability to get depth test function +RenderingAttributes16=RenderingAttributes: no capability to set stencil attributes +RenderingAttributes17=RenderingAttributes: no capability to get stencil attributes +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: illegal boundary width +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 +Texture46=Texture: illegal width < 1 +Texture47=Texture: illegal height < 1 +Texture48=Texture: maximumLevel must be zero if mipmapMode is BASE_LEVEL +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 +MasterControl2=NOTE: simulated multi-texture will not work for programmable shaders +MasterControl3=and will be removed entirely in the next release of Java 3D +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 +J3DGraphics2D0=Cannot use Graphics2D object after dispose() is called +GLSLShaderProgram0=GLSLShaderProgram: no capability to read names +GLSLShaderProgram1=GLSLShaderProgram: no capability to read shaders +GLSLShaderProgram2=GLSLShaderProgram: Shader has incompatible shading language +CgShaderProgram0=CgShaderProgram: no capability to read names +CgShaderProgram1=CgShaderProgram: no capability to read shaders +CgShaderProgram2=CgShaderProgram: Shader has incompatible shading language +CgShaderProgram3=CgShaderProgram: must not specify more than one vertex shader +CgShaderProgram4=CgShaderProgram: must not specify more than one fragment shader +ShaderAppearance0=ShaderAppearance: no capability to set shader program +ShaderAppearance1=ShaderAppearance: no capability to get shader program +ShaderAppearance2=ShaderAppearance: no capability to set shader attribute set +ShaderAppearance3=ShaderAppearance: no capability to get shader attribute set +ShaderAttributeBinding0=ShaderAttributeBinding is not supported +ShaderProgramRetained0=Shading language not supported +ShaderAttributeObject0=ShaderAttributeObject: no capability to get value +ShaderAttributeObject1=ShaderAttributeObject: no capability to set value +ShaderAttributeSet0=ShaderAttributeSet: no capability to get attribute +ShaderAttributeSet1=ShaderAttributeSet: no capability to set attribute diff --git a/src/javax/media/j3d/ExponentialFog.java b/src/javax/media/j3d/ExponentialFog.java new file mode 100644 index 0000000..bc24ee3 --- /dev/null +++ b/src/javax/media/j3d/ExponentialFog.java @@ -0,0 +1,228 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * The fog blending factor, f, is computed as follows: + *

    + * f = e-(density * z)

    + * where + *

      z is the distance from the viewpoint.
      + * density is the density of the fog.

+ * + * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_DENSITY_READ + }; + + /** + * Constructs an ExponentialFog node with default parameters. + * The default values are as follows: + *
    + * density : 1.0
    + *
+ */ + public ExponentialFog() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs an ExponentialFog node with the specified fog color. + * @param color the fog color + */ + public ExponentialFog(Color3f color) { + super(color); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + void createRetained() { + this.retained = new ExponentialFogRetained(); + this.retained.setSource(this); + } + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + ExponentialFog ef = new ExponentialFog(); + ef.duplicateNode(this, forceDuplicate); + return ef; + } + + + /** + * Copies all ExponentialFog information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + super.duplicateAttributes(originalNode, forceDuplicate); + + ((ExponentialFogRetained) retained).initDensity( + ((ExponentialFogRetained) originalNode.retained).getDensity()); + } +} diff --git a/src/javax/media/j3d/ExponentialFogRetained.java b/src/javax/media/j3d/ExponentialFogRetained.java new file mode 100644 index 0000000..e4c0945 --- /dev/null +++ b/src/javax/media/j3d/ExponentialFogRetained.java @@ -0,0 +1,183 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3f; + +/** + * The ExponentialFog leaf node defines distance parameters for + * exponential fog. + */ +class ExponentialFogRetained extends FogRetained { + // Fog density + private float density = 1.0f; + + // Issue 144: density in Eye Coordinates (EC) + private float densityInEc; + + // 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 = new J3dMessage(); + 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; + } + + + @Override + void setLive(SetLiveState s) { + super.setLive(s); + + // Initialize the mirror object, this needs to be done, when + // renderBin is not accessing any of the fields + J3dMessage createMessage = new J3dMessage(); + 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++) { + GroupRetained group = 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. + */ + @Override + void update(Context ctx, double scale) { + // Issue 144: recompute the density in EC, and send it to native code + validateDistancesInEc(scale); + Pipeline.getPipeline().updateExponentialFog(ctx, color.x, color.y, color.z, densityInEc); + } + + + + // The update Object function. + // Note : if you add any more fields here , you need to update + // updateFog() in RenderingEnvironmentStructure + @Override + 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(); + + } + // Issue 144: store the local to vworld scale used to transform the density + ((ExponentialFogRetained)mirrorFog).setLocalToVworldScale(getLastLocalToVworld().getDistanceScale()); + + super.updateMirrorObject(objs); + } + + + // Clone the retained side only, internal use only + @Override + protected Object clone() { + ExponentialFogRetained efr = + (ExponentialFogRetained)super.clone(); + + efr.initDensity(getDensity()); + + return efr; + } + + // Issue 144: method to recompute the density in EC by multiplying the specified + // density by the inverse of the local to EC scale + /** + * Scale distances from local to eye coordinate. + */ + @Override + protected void validateDistancesInEc(double vworldToCoexistenceScale) { + // vworldToCoexistenceScale can be used here since + // CoexistenceToEc has a unit scale + double localToEcScale = getLocalToVworldScale() * vworldToCoexistenceScale; + + densityInEc = (float)(density / localToEcScale); + } + +} diff --git a/src/javax/media/j3d/Fog.java b/src/javax/media/j3d/Fog.java new file mode 100644 index 0000000..58557a9 --- /dev/null +++ b/src/javax/media/j3d/Fog.java @@ -0,0 +1,568 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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). + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_INFLUENCING_BOUNDS_READ, + ALLOW_COLOR_READ, + ALLOW_SCOPE_READ + }; + + /** + * Constructs a Fog node with default parameters. The default + * values are as follows: + *

    + * color : black (0,0,0)
    + * scope : empty (universe scope)
    + * influencing bounds : null
    + * influencing bounding leaf : null
    + *
+ */ + public Fog() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs a Fog node with the specified fog color. + * @param color the fog color + */ + public Fog(Color3f color) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ((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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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(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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/FogRetained.java b/src/javax/media/j3d/FogRetained.java new file mode 100644 index 0000000..32cfae5 --- /dev/null +++ b/src/javax/media/j3d/FogRetained.java @@ -0,0 +1,815 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Color3f; + +/** + * 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; + + // Scale value extracted from localToVworld transform + private double localToVworldScale = 1.0; + + FogRetained() { + localBounds = new BoundingBox((Bounds)null); + } + + /** + * 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(); + Object[] scopeInfo = new Object[3]; + + + GroupRetained group = 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 = scopes.elementAt(index); + scopes.removeElementAt(index); + group.removeFogScope(); + + } + + void removeScope(int index) { + + Object[] scopeInfo = new Object[3]; + ArrayList removeScopeList = new ArrayList(); + GroupRetained group = 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)(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((Group)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(group); + 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(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(); + int n = scopes.size(); + + tempKey.reset(); + for (int index = n - 1; index >= 0; index--) { + GroupRetained group = 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(Context ctx, double scale); + + + void updateImmediateMirrorObject(Object[] objs) { + int i; + + 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 = 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 = mirrorFog.boundingLeaf.transformedRegion; + } + else { + if (mirrorFog.regionOfInfluence != null) { + mirrorFog.region = 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. + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + void setLive(SetLiveState s) { + 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 = 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]; + + 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 + */ + @Override + void clearLive(SetLiveState s) { + int i; + 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 = new J3dMessage(); + 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 = 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 + @Override + 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; + } + + @Override + void updateTransformChange() { + super.updateTransformChange(); + setLocalToVworldScale(sgFog.getLastLocalToVworld().getDistanceScale()); + } + + // 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 = new J3dMessage(); + 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); + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + if (regionOfInfluence != null) { + regionOfInfluence.transform(xform.transform); + } + } + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + leafList.add(mirrorFog); + } + + /** + * Scale distances from local to eye coordinate + */ + protected void validateDistancesInEc(double vworldToCoexistenceScale) { + assert false : "subclasses should override this method"; + } + + double getLocalToVworldScale() { + return localToVworldScale; + } + + void setLocalToVworldScale(double localToVworldScale) { + this.localToVworldScale = localToVworldScale; + } +} diff --git a/src/javax/media/j3d/Font3D.java b/src/javax/media/j3d/Font3D.java new file mode 100644 index 0000000..7dc3a44 --- /dev/null +++ b/src/javax/media/j3d/Font3D.java @@ -0,0 +1,1184 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Font; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; + +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +import com.sun.j3d.utils.geometry.GeometryInfo; +import com.sun.j3d.utils.geometry.NormalGenerator; + +/** + * The Font3D object is used to store extruded 2D glyphs. These + * 3D glyphs can then be used to construct Text3D NodeComponent + * objects. + *

+ * 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. + *

+ * 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). + *

+ * 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. + *

+ * 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: + * + *

    + * tessellation tolerance : 0.01
    + *
+ *

+ * 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 flatness parameter in + * the java.awt.Shape.getPathIterator 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); + } + +/** + * An append-only array-based integer list + */ +private static class IntVector { + int[] data; + int size; + + IntVector() { + data = new int[10]; + size = 0; + } + + void add(int i) { + // need to expand backing + if (size == data.length) + data = Arrays.copyOf(data, 2 * size); + + data[size] = i; + size++; + } +} + + // BY MIK OF CLASSX + /** + * Returns a GeometryArray of a glyph in this Font3D. + * + * @param c character from which to generate a tessellated glyph. + * + * @return a GeometryArray + * + * @since Java 3D 1.4 + */ + public GeometryArray getGlyphGeometry(char c) { + char code[] = { c }; + GlyphVector gv = font.createGlyphVector(frc, code); + + // triangulate the glyph + GeometryArrayRetained glyph_gar = triangulateGlyphs(gv, code[0]); + + // Assume that triangulateGlyphs returns a triangle array with only coords & normals + // (and without by-ref, interleaved, etc.) + assert glyph_gar instanceof TriangleArrayRetained : + "Font3D: GeometryArray is not an instance of TrangleArray"; + assert glyph_gar.getVertexFormat() == (GeometryArray.COORDINATES | GeometryArray.NORMALS) : + "Font3D: Illegal GeometryArray format -- only coordinates and normals expected"; + + // create a correctly sized TriangleArray + TriangleArray ga = new TriangleArray(glyph_gar.getVertexCount(),glyph_gar.getVertexFormat()); + + // temp storage for coords, normals + float tmp[] = new float[3]; + + int vertexCount = ga.getVertexCount(); + for(int i=0; i 0) { + if (setMaxY) { + // Get Previous point + beginIdx = start; + endIdx = numPoints-1; + } + contours.add(num); + num = 0; + } + } 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.add(num); + num = 0; + } + 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); + + for (int cIdx = 0; cIdx < contours.size; cIdx++) { + endIdx = startIdx + contours.data[cIdx]; + islandsTree.insert(new IslandsNode(startIdx, endIdx), vertices); + startIdx = endIdx; + } + + coords = null; // Free memory + contours = 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; + + int[] 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. + NormalGenerator ng = new NormalGenerator(); + for (i = 0; i < islandCounts.length; i++) { + contourCounts[0] = islandCounts[i].length; + numPoints += outVerts[i].length; + GeometryInfo 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); + } + } + + // XXXX: 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 = 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; + 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 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/javax/media/j3d/FontExtrusion.java b/src/javax/media/j3d/FontExtrusion.java new file mode 100644 index 0000000..d326154 --- /dev/null +++ b/src/javax/media/j3d/FontExtrusion.java @@ -0,0 +1,255 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import java.awt.Shape; +import java.awt.geom.PathIterator; +import java.util.ArrayList; + +import javax.vecmath.Point2f; + + /** + * 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. + *

+ * 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. + *

+ * 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: + * + *

    + * extrusion shape : null
    + * tessellation tolerance : 0.01
    + *
+ * + * 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 flatness parameter in + * the java.awt.Shape.getPathIterator 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.err.println("num "+num+" inc "+inc); + if (inc == 0){ + for (i=0;i < num;i++){ + pnts[i] = (Point2f)coords.get(i); + //System.err.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.err.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/javax/media/j3d/FreeListManager.java b/src/javax/media/j3d/FreeListManager.java new file mode 100644 index 0000000..83e2c66 --- /dev/null +++ b/src/javax/media/j3d/FreeListManager.java @@ -0,0 +1,84 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +class FreeListManager { + + private static final boolean DEBUG = false; + + // constants that represent the freelists managed by the Manager + static final int DISPLAYLIST = 0; + + private static int maxFreeListNum = 0; + + // what list we are going to shrink next + private static int currlist = 0; + + static MemoryFreeList[] freelist = null; + + static void createFreeLists() { + maxFreeListNum = 0; + freelist = new MemoryFreeList[maxFreeListNum+1]; + freelist[DISPLAYLIST] = new IntegerFreeList(); + } + + // see if the current list can be shrunk + static void manageLists() { +// System.err.println("manageLists"); + if (freelist[currlist] != null) { + freelist[currlist].shrink(); + } + + currlist++; + if (currlist > maxFreeListNum) currlist = 0; + } + + // return the freelist specified by the list param + static MemoryFreeList getFreeList(int list) { + if (list < 0 || list > maxFreeListNum) { + if (DEBUG) System.err.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/javax/media/j3d/GLSLShaderProgram.java b/src/javax/media/j3d/GLSLShaderProgram.java new file mode 100644 index 0000000..6e5ef02 --- /dev/null +++ b/src/javax/media/j3d/GLSLShaderProgram.java @@ -0,0 +1,179 @@ +/* + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The GLSLShaderProgram object is a concrete implementation of a + * ShaderProgram node component for the OpenGL GLSL shading language. + * + * @see SourceCodeShader + * + * @since Java 3D 1.4 + */ + +public class GLSLShaderProgram extends ShaderProgram { + + /** + * Constructs a GLSL shader program node component. + * + *
+ * TODO: ADD MORE DOCUMENTATION HERE. + */ + public GLSLShaderProgram() { + } + + // Implement abstract setVertexAttrNames method (inherit javadoc from parent class) + @Override + public void setVertexAttrNames(String[] vertexAttrNames) { + checkForLiveOrCompiled(); + + if (vertexAttrNames != null) { + for (int i = 0; i < vertexAttrNames.length; i++) { + if (vertexAttrNames[i] == null) { + throw new NullPointerException(); + } + } + } + + ((GLSLShaderProgramRetained)this.retained).setVertexAttrNames(vertexAttrNames); + } + + // Implement abstract getVertexAttrNames method (inherit javadoc from parent class) + @Override + public String[] getVertexAttrNames() { + + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_NAMES_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GLSLShaderProgram0")); + } + } + + return ((GLSLShaderProgramRetained)this.retained).getVertexAttrNames(); + + } + + // Implement abstract setShaderAttrNames method (inherit javadoc from parent class) + @Override + public void setShaderAttrNames(String[] shaderAttrNames) { + checkForLiveOrCompiled(); + + if (shaderAttrNames != null) { + for (int i = 0; i < shaderAttrNames.length; i++) { + if (shaderAttrNames[i] == null) { + throw new NullPointerException(); + } + } + } + + ((GLSLShaderProgramRetained)this.retained).setShaderAttrNames(shaderAttrNames); + } + + // Implement abstract getShaderAttrNames method (inherit javadoc from parent class) + @Override + public String[] getShaderAttrNames() { + + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_NAMES_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GLSLShaderProgram0")); + } + } + + return ((GLSLShaderProgramRetained)this.retained).getShaderAttrNames(); + + } + + /** + * Copies the specified array of shaders into this shader + * program. This method makes a shallow copy of the array. The + * array of shaders may be null or empty (0 length), but the + * elements of the array must be non-null. The shading language of + * each shader in the array must be + * SHADING_LANGUAGE_GLSL. Each shader in the array must + * be a SourceCodeShader. + * + * @param shaders array of Shader objects to be copied into this + * ShaderProgram + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalArgumentException if the shading language of + * any shader in the shaders array is not + * SHADING_LANGUAGE_GLSL. + * + * @exception ClassCastException if any shader in the shaders + * array is not a SourceCodeShader. + * + * @exception NullPointerException if any element in the + * shaders array is null. + */ + @Override + public void setShaders(Shader[] shaders) { + checkForLiveOrCompiled(); + + if(shaders != null) { + // Check shaders for valid shading language and class type + for (int i = 0; i < shaders.length; i++) { + if (shaders[i].getShadingLanguage() != Shader.SHADING_LANGUAGE_GLSL) { + throw new IllegalArgumentException(J3dI18N.getString("GLSLShaderProgram2")); + } + + // Try to cast shader to SourceCodeShader; it will throw + // ClassCastException if it isn't. + SourceCodeShader shad = (SourceCodeShader)shaders[i]; + } + + } + + ((GLSLShaderProgramRetained)this.retained).setShaders(shaders); + } + + // Implement abstract getShaders method (inherit javadoc from parent class) + @Override + public Shader[] getShaders() { + + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_SHADERS_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GLSLShaderProgram1")); + } + } + + return ((GLSLShaderProgramRetained)this.retained).getShaders(); + } + + /** + * Creates a retained mode GLSLShaderProgramRetained object that this + * GLSLShaderProgram component object will point to. + */ + @Override + void createRetained() { + this.retained = new GLSLShaderProgramRetained(); + this.retained.setSource(this); + } + + +} diff --git a/src/javax/media/j3d/GLSLShaderProgramRetained.java b/src/javax/media/j3d/GLSLShaderProgramRetained.java new file mode 100644 index 0000000..d1c7614 --- /dev/null +++ b/src/javax/media/j3d/GLSLShaderProgramRetained.java @@ -0,0 +1,429 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The GLSLShaderProgram object is a concrete implementation of a + * ShaderProgram node component for the OpenGL GLSL shading language. + */ + +class GLSLShaderProgramRetained extends ShaderProgramRetained { + + /** + * Constructs a GLSL shader program node component. + */ + GLSLShaderProgramRetained() { + } + + @Override + synchronized void createMirrorObject() { + // System.err.println("GLSLShaderProgramRetained : createMirrorObject"); + // This method should only call by setLive(). + if (mirror == null) { + GLSLShaderProgramRetained mirrorGLSLSP = new GLSLShaderProgramRetained(); + mirror = mirrorGLSLSP; + mirror.source = source; + } + initMirrorObject(); + } + + // ShaderAttributeValue methods + + @Override + ShaderError setUniform1i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int value) { + + return Pipeline.getPipeline().setGLSLUniform1i(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform1f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float value) { + + return Pipeline.getPipeline().setGLSLUniform1f(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform2i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform2i(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform2f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform2f(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform3i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform3i(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform3f(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform4i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform4i(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniform4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform4f(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniformMatrix3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniformMatrix3f(ctx, + shaderProgramId, + uniformLocation, + value); + } + + @Override + ShaderError setUniformMatrix4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniformMatrix4f(ctx, + shaderProgramId, + uniformLocation, + value); + } + + // ShaderAttributeArray methods + + @Override + ShaderError setUniform1iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform1iArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform1fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform1fArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform2iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform2iArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform2fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform2fArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform3iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform3iArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform3fArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform4iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + + return Pipeline.getPipeline().setGLSLUniform4iArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniform4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniform4fArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniformMatrix3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniformMatrix3fArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + @Override + ShaderError setUniformMatrix4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + + return Pipeline.getPipeline().setGLSLUniformMatrix4fArray(ctx, + shaderProgramId, + uniformLocation, + numElements, + value); + } + + /** + * Method to return a flag indicating whether this + * ShaderProgram is supported on the specified Canvas. + */ + @Override + boolean isSupported(Canvas3D cv) { + return cv.shadingLanguageGLSL; + } + + /** + * Method to create the native shader. + */ + @Override + ShaderError createShader(Context ctx, ShaderRetained shader, ShaderId[] shaderIdArr) { + return Pipeline.getPipeline().createGLSLShader(ctx, shader.shaderType, shaderIdArr); + } + + /** + * Method to destroy the native shader. + */ + @Override + ShaderError destroyShader(Context ctx, ShaderId shaderId) { + return Pipeline.getPipeline().destroyGLSLShader(ctx, shaderId); + } + + /** + * Method to compile the native shader. + */ + @Override + ShaderError compileShader(Context ctx, ShaderId shaderId, String source) { + return Pipeline.getPipeline().compileGLSLShader(ctx, shaderId, source ); + } + + /** + * Method to create the native shader program. + */ + @Override + ShaderError createShaderProgram(Context ctx, ShaderProgramId[] shaderProgramIdArr) { + return Pipeline.getPipeline().createGLSLShaderProgram(ctx, shaderProgramIdArr); + } + + /** + * Method to destroy the native shader program. + */ + @Override + ShaderError destroyShaderProgram(Context ctx, ShaderProgramId shaderProgramId) { + return Pipeline.getPipeline().destroyGLSLShaderProgram(ctx, shaderProgramId); + } + + /** + * Method to link the native shader program. + */ + @Override + ShaderError linkShaderProgram(Context ctx, ShaderProgramId shaderProgramId, ShaderId[] shaderIds) { + return Pipeline.getPipeline().linkGLSLShaderProgram(ctx, shaderProgramId, shaderIds); + } + + @Override + ShaderError bindVertexAttrName(Context ctx, ShaderProgramId shaderProgramId, String attrName, int attrIndex) { + return Pipeline.getPipeline().bindGLSLVertexAttrName(ctx, shaderProgramId, attrName, attrIndex); + } + + @Override + void lookupVertexAttrNames(Context ctx, ShaderProgramId shaderProgramId, String[] attrNames, boolean[] errArr) { + // This method is a no-op for GLSL + } + + @Override + void lookupShaderAttrNames(Context ctx, ShaderProgramId shaderProgramId, + String[] attrNames, AttrNameInfo[] attrNameInfoArr) { + + int numAttrNames = attrNames.length; + + ShaderAttrLoc[] locArr = new ShaderAttrLoc[numAttrNames]; + int[] typeArr = new int[numAttrNames]; + int[] sizeArr = new int[numAttrNames]; // currently unused + boolean[] isArrayArr = new boolean[numAttrNames]; + + Pipeline.getPipeline().lookupGLSLShaderAttrNames(ctx, shaderProgramId, + numAttrNames, attrNames, locArr, typeArr, sizeArr, isArrayArr); + + for (int i = 0; i < numAttrNames; i++) { + attrNameInfoArr[i] = new AttrNameInfo(); + attrNameInfoArr[i].setLocation(locArr[i]); + attrNameInfoArr[i].setArray(isArrayArr[i]); + attrNameInfoArr[i].setType(typeArr[i]); +// System.err.println(attrNames[i] + +// " : loc = " + locArr[i] + +// ", type = " + typeArr[i] + +// ", isArray = " + isArrayArr[i] + +// ", size = " + sizeArr[i]); + } + } + + /** + * Method to enable the native shader program. + */ + @Override + ShaderError enableShaderProgram(Context ctx, ShaderProgramId shaderProgramId) { + return Pipeline.getPipeline().useGLSLShaderProgram(ctx, shaderProgramId); + } + + /** + * Method to disable the native shader program. + */ + @Override + ShaderError disableShaderProgram(Context ctx) { + return Pipeline.getPipeline().useGLSLShaderProgram(ctx, null); + } + + +} diff --git a/src/javax/media/j3d/GeneralizedStrip.java b/src/javax/media/j3d/GeneralizedStrip.java new file mode 100644 index 0000000..baddedf --- /dev/null +++ b/src/javax/media/j3d/GeneralizedStrip.java @@ -0,0 +1,889 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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.err.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 ; + } + + @Override + 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.err.println("GeneralizedStrip.toStripsAndFans") ; + if (v > size) + System.err.println(" ended with a degenerate triangle:" + + " number of vertices: " + (v-size)) ; + + System.err.println("\n number of strips: " + stripCounts.count) ; + if (stripCounts.count > 0) { + System.err.println(" number of vertices: " + stripVerts.count) ; + System.err.println(" vertices/strip: " + + (float)stripVerts.count/stripCounts.count) ; + System.err.println(" strip counts: " + stripCounts.toString()) ; + // System.err.println(" indices: " + stripVerts.toString()) ; + } + + System.err.println("\n number of fans: " + fanCounts.count) ; + if (fanCounts.count > 0) { + System.err.println(" number of vertices: " + fanVerts.count) ; + System.err.println(" vertices/strip: " + + (float)fanVerts.count/fanCounts.count) ; + System.err.println(" fan counts: " + fanCounts.toString()) ; + // System.err.println(" indices: " + fanVerts.toString()) ; + } + System.err.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.err.println("GeneralizedStrip.toTriangleStrips" + + "\n number of strips: " + + sa.stripCounts.count) ; + if (sa.stripCounts.count > 0) { + System.err.println(" number of vertices: " + + sa.vertices.count + + "\n vertices/strip: " + + ((float)sa.vertices.count / + (float)sa.stripCounts.count)) ; + System.err.print(" strip counts: [") ; + for (i = 0 ; i < sa.stripCounts.count-1 ; i++) + System.err.print(sa.stripCounts.ints[i] + ", ") ; + System.err.println(sa.stripCounts.ints[i] + "]") ; + } + System.err.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.err.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.err.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.err.println(" triangles separated: " + triangleCount) ; + if (longStripCount > 0) { + System.err.println + (" new vertices/strip: " + + ((float)longStripVertexCount/(float)longStripCount)) ; + + System.err.print(" long strip counts: [") ; + for (int i = 0 ; i < longStripCount-1 ; i++) + System.err.print(newStripCounts[i++] + ", ") ; + + System.err.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.err.println("GeneralizedStrip.toLineStrips\n") ; + if (v > size) + System.err.println(" ended with a degenerate line") ; + + System.err.println(" number of strips: " + stripCounts.count) ; + if (stripCounts.count > 0) { + System.err.println(" number of vertices: " + stripVerts.count) ; + System.err.println(" vertices/strip: " + + (float)stripVerts.count/stripCounts.count) ; + System.err.println(" strip counts: " + stripCounts.toString()) ; + // System.err.println(" indices: " + stripVerts.toString()) ; + } + System.err.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/javax/media/j3d/GeneralizedStripFlags.java b/src/javax/media/j3d/GeneralizedStripFlags.java new file mode 100644 index 0000000..330342d --- /dev/null +++ b/src/javax/media/j3d/GeneralizedStripFlags.java @@ -0,0 +1,87 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/GeneralizedVertexList.java b/src/javax/media/j3d/GeneralizedVertexList.java new file mode 100644 index 0000000..26a225b --- /dev/null +++ b/src/javax/media/j3d/GeneralizedVertexList.java @@ -0,0 +1,406 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import java.util.ArrayList; + +import javax.vecmath.Color3f; +import javax.vecmath.Color4f; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +/** + * 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 + @Override + public int getFlagCount() { + return vertices.size() ; + } + + // GeneralizedStripFlags interface implementation + @Override + public int getFlag(int index) { + return 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) { + 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++) { + Vertex v = 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++) { + Vertex v = 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++) { + Vertex v = 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++) { + Vertex v = 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++) { + Vertex v = 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++) { + Vertex v = 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/javax/media/j3d/Geometry.java b/src/javax/media/j3d/Geometry.java new file mode 100644 index 0000000..ac6b3cf --- /dev/null +++ b/src/javax/media/j3d/Geometry.java @@ -0,0 +1,67 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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:

+ *

  • Compressed Geometry
  • + *
  • GeometryArray
  • + *
  • Raster
  • + *
  • Text3D
  • + *

+ * 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. This + * capability bit is set (true) by default for all Geometry objects. + */ + public static final int + ALLOW_INTERSECT = CapabilityBits.GEOMETRY_ALLOW_INTERSECT; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_INTERSECT + }; + + /** + * Constructs a new Geometry object. + */ + public Geometry() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } +} diff --git a/src/javax/media/j3d/GeometryArray.java b/src/javax/media/j3d/GeometryArray.java new file mode 100644 index 0000000..f830acc --- /dev/null +++ b/src/javax/media/j3d/GeometryArray.java @@ -0,0 +1,7467 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Color3b; +import javax.vecmath.Color3f; +import javax.vecmath.Color4b; +import javax.vecmath.Color4f; +import javax.vecmath.Point2f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Point4f; +import javax.vecmath.TexCoord2f; +import javax.vecmath.TexCoord3f; +import javax.vecmath.TexCoord4f; +import javax.vecmath.Vector3f; + + +/** + * The GeometryArray object contains separate arrays of positional + * coordinates, colors, normals, texture coordinates, and vertex + * attributes 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. + *

+ *

    + *
  • + * By Copying: + * The existing methods for setting positional coordinates, colors, + * normals, texture coordinates, and vertex attributes + * (such as setCoordinate, + * setColors, 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. + *
  • + *
  • By Reference: + * 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 BY_REFERENCE bit in the + * vertexFormat field of the constructor for this + * GeometryArray. In this mode, the various set methods for + * coordinates, normals, colors, texture coordinates, and vertex attributes + * are not used. + * Instead, new methods are used to set a reference to user-supplied + * coordinate, color, normal, texture coordinate, and vertex attribute + * arrays (such as + * setCoordRefFloat, setColorRefFloat, + * etc.). Data in any array that is referenced by a live or compiled + * GeometryArray object may only be modified via the + * updateData method (subject to the + * ALLOW_REF_DATA_WRITE capability bit). Applications + * must exercise care not to violate this rule. If any referenced + * geometry data is modified outside of the updateData + * method, the results are undefined. + *
  • + *
+ *

+ * 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. + *

+ * Note that the term coordinate, as used in the method names + * and method descriptions, actually refers to a set of x, + * y, and z coordinates representing the position of a + * single vertex. The term coordinates (plural) is used to + * indicate sets of x, y, and z 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 texture coordinate is used to indicate a + * set of texture coordinates for a single vertex, while the term + * texture coordinates (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 array of + * vertex attributes. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_VERTEX_ATTR_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_READ; + + /** + * Specifies that this GeometryArray allows writing the array of + * vertex attributes. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_VERTEX_ATTR_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_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 + * BY_REFERENCE 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 BY_REFERENCE + * flag. + * + * @see J3DBuffer + * @see #setCoordRefBuffer(J3DBuffer) + * @see #setColorRefBuffer(J3DBuffer) + * @see #setNormalRefBuffer(J3DBuffer) + * @see #setTexCoordRefBuffer(int,J3DBuffer) + * @see #setVertexAttrRefBuffer(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 five vertex components (coord, color, + * normal, texCoord, and vertexAttr). The color, normal, texCoord, + * and vertexAttr index arrays are neither allocated nor used. Any + * attempt to access the color, normal, texCoord, + * or vertexAttr index arrays will result in a NullPointerException. + * 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; + + /** + * Specifies that this GeometryArray contains one or more arrays of + * vertex attributes. These attributes are used in programmable + * shading. + * + * @since Java 3D 1.4 + */ + public static final int VERTEX_ATTRIBUTES = 0x1000; + + //NVaidya + /** + * Specifies that the indices in this GeometryArray + * are accessed by reference. This flag is only valid for + * indexed geometry arrays (subclasses of IndexedGeometryArray) and only + * when used in conjunction with the BY_REFERENCE and + * USE_COORD_INDEX_ONLY flags. + * + * @since Java 3D 1.5 + */ + public static final int BY_REFERENCE_INDICES = 0x2000; + + // Used to keep track of the last bit (for adding new bits only) + private static final int LAST_FORMAT_BIT = 0x2000; + + // 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; + + private static final int[] defTexCoordMap = { 0 }; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_COLOR_READ, + ALLOW_COORDINATE_READ, + ALLOW_COUNT_READ, + ALLOW_FORMAT_READ, + ALLOW_NORMAL_READ, + ALLOW_REF_DATA_READ, + ALLOW_TEXCOORD_READ, + ALLOW_VERTEX_ATTR_READ + }; + + + // non-public, no parameter constructor + GeometryArray() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + //NVaidya + /** + * 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: + *

    + * texCoordSetCount : 1
    + * texCoordSetMap : { 0 }
    + * vertexAttrCount : 0
    + * vertexAttrSizes : null
    + * validVertexCount : vertexCount
    + * initialVertexIndex : 0
    + * initialCoordIndex : 0
    + * initialColorIndex : 0
    + * initialNormalIndex : 0
    + * initialTexCoordIndex : 0
    + * initialVertexAttrIndex : 0
    + * all data array values : 0.0
    + * all data array references : null
    + *
+ * + * @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: COORDINATES, to signal the inclusion of + * vertex positions--always present; NORMALS, to signal + * the inclusion of per vertex normals; one of COLOR_3 or + * COLOR_4, to signal the inclusion of per vertex + * colors (without or with alpha 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); + * BY_REFERENCE, to indicate that the data is passed + * by reference + * rather than by copying; INTERLEAVED, to indicate + * that the referenced + * data is interleaved in a single array; + * USE_NIO_BUFFER, to indicate that the referenced data + * is accessed via a J3DBuffer object that wraps an NIO buffer; + * USE_COORD_INDEX_ONLY, + * to indicate that only the coordinate indices are used for indexed + * geometry arrays; + * BY_REFERENCE_INDICES, to indicate + * that the indices are accessed by reference in indexed + * geometry arrays.

+ * + * @exception IllegalArgumentException if vertexCount < 0 + * + * @exception IllegalArgumentException if vertexFormat does not + * include COORDINATES + * + * @exception IllegalArgumentException if the USE_COORD_INDEX_ONLY + * bit or the BY_REFERENCE_INDICES bit is set for + * non-indexed geometry arrays (that is, GeometryArray objects + * that are not a subclass of IndexedGeometryArray) + * + * @exception IllegalArgumentException if the INTERLEAVED + * bit is set without the BY_REFERENCE bit being set + * + * @exception IllegalArgumentException if the USE_NIO_BUFFER + * bit is set without the BY_REFERENCE bit being set + * + * @exception IllegalArgumentException if the INTERLEAVED + * bit and the VERTEX_ATTRIBUTES bit are both set + * + * @exception IllegalArgumentException if the + * BY_REFERENCE_INDICES + * bit is set without the BY_REFERENCE and + * USE_COORD_INDEX_ONLY bits being set + */ + public GeometryArray(int vertexCount, int vertexFormat) { + this(vertexCount, vertexFormat, + ((vertexFormat & TEXTURE_COORDINATE) != 0 ? 1 : 0), + ((vertexFormat & TEXTURE_COORDINATE) != 0 ? defTexCoordMap : null)); + } + + + //NVaidya + /** + * 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

+ * + * @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 or + * COLOR_4, to signal the inclusion of per vertex + * colors (without or with alpha information); one of + * TEXTURE_COORDINATE_2 or TEXTURE_COORDINATE_3 + * or TEXTURE_COORDINATE_4, + * to signal the + * inclusion of per-vertex texture coordinates (2D , 3D or 4D); + * BY_REFERENCE, to indicate that the data is passed + * by reference + * rather than by copying; INTERLEAVED, to indicate + * that the referenced + * data is interleaved in a single array; + * USE_NIO_BUFFER, to indicate that the referenced data + * is accessed via a J3DBuffer object that wraps an NIO buffer; + * USE_COORD_INDEX_ONLY, + * to indicate that only the coordinate indices are used for indexed + * geometry arrays; + * BY_REFERENCE_INDICES, to indicate + * that the indices are accessed by reference in indexed + * geometry arrays.

+ * + * @param texCoordSetCount the number of texture coordinate sets + * in this GeometryArray object. If vertexFormat + * does not include one of TEXTURE_COORDINATE_2 or + * TEXTURE_COORDINATE_3, the + * texCoordSetCount parameter is not used.

+ * + * + * @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 + * texCoordSetCount. 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 + * vertexFormat does not include one of + * TEXTURE_COORDINATE_2 or + * TEXTURE_COORDINATE_3 or + * TEXTURE_COORDINATE_4, the + * texCoordSetMap array is not used. The following example + * illustrates the use of the texCoordSetMap array. + * + *

+ *

+ *

+ * + * @exception IllegalArgumentException if vertexCount < 0 + * + * @exception IllegalArgumentException if vertexFormat does not + * include COORDINATES + * + * @exception IllegalArgumentException if the USE_COORD_INDEX_ONLY + * bit or the BY_REFERENCE_INDICES bit is set for + * non-indexed geometry arrays (that is, GeometryArray objects + * that are not a subclass of IndexedGeometryArray) + * + * @exception IllegalArgumentException if the INTERLEAVED + * bit is set without the BY_REFERENCE bit being set + * + * @exception IllegalArgumentException if the USE_NIO_BUFFER + * bit is set without the BY_REFERENCE bit being set + * + * @exception IllegalArgumentException if the INTERLEAVED + * bit and the VERTEX_ATTRIBUTES bit are both set + * + * @exception IllegalArgumentException if the + * BY_REFERENCE_INDICES + * bit is set without the BY_REFERENCE and + * USE_COORD_INDEX_ONLY bits being set + * + * @exception IllegalArgumentException if + * texCoordSetCount < 0 + * + * @exception IllegalArgumentException if any element in + * texCoordSetMap[] >= texCoordSetCount. + * + * @since Java 3D 1.2 + */ + public GeometryArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap) { + this(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, 0, null); + } + + + //NVaidya + /** + * Constructs an empty GeometryArray object with the specified + * number of vertices, vertex format, number of texture coordinate + * sets, texture coordinate mapping array, vertex attribute count, + * and vertex attribute sizes array. + * + * @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: COORDINATES, to signal the inclusion of + * vertex positions--always present; NORMALS, to signal + * the inclusion of per vertex normals; one of COLOR_3 or + * COLOR_4, to signal the inclusion of per vertex + * colors (without or with alpha information); one of + * TEXTURE_COORDINATE_2 or TEXTURE_COORDINATE_3 + * or TEXTURE_COORDINATE_4, + * to signal the + * inclusion of per-vertex texture coordinates (2D , 3D or 4D); + * VERTEX_ATTRIBUTES, to signal + * the inclusion of one or more arrays of vertex attributes; + * BY_REFERENCE, to indicate that the data is passed + * by reference + * rather than by copying; INTERLEAVED, to indicate + * that the referenced + * data is interleaved in a single array; + * USE_NIO_BUFFER, to indicate that the referenced data + * is accessed via a J3DBuffer object that wraps an NIO buffer; + * USE_COORD_INDEX_ONLY, + * to indicate that only the coordinate indices are used for indexed + * geometry arrays; + * BY_REFERENCE_INDICES, to indicate + * that the indices are accessed by reference in indexed + * geometry arrays.

+ * + * @param texCoordSetCount the number of texture coordinate sets + * in this GeometryArray object. If vertexFormat + * does not include one of TEXTURE_COORDINATE_2 or + * TEXTURE_COORDINATE_3, the + * texCoordSetCount parameter is not used.

+ * + * + * @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 + * texCoordSetCount. 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 + * vertexFormat does not include one of + * TEXTURE_COORDINATE_2 or + * TEXTURE_COORDINATE_3 or + * TEXTURE_COORDINATE_4, the + * texCoordSetMap array is not used. The following example + * illustrates the use of the texCoordSetMap array. + * + *

+ *

+ *

+ * + * @param vertexAttrCount the number of vertex attributes + * in this GeometryArray object. If vertexFormat + * does not include VERTEX_ATTRIBUTES, the + * vertexAttrCount parameter must be 0.

+ * + * @param vertexAttrSizes is an array that specifes the size of + * each vertex attribute. Each element in the array specifies the + * number of components in the attribute, from 1 to 4. The length + * of the array must be equal to vertexAttrCount.

+ * + * @exception IllegalArgumentException if vertexCount < 0 + * + * @exception IllegalArgumentException if vertexFormat does not + * include COORDINATES + * + * @exception IllegalArgumentException if the USE_COORD_INDEX_ONLY + * bit or the BY_REFERENCE_INDICES bit is set for + * non-indexed geometry arrays (that is, GeometryArray objects + * that are not a subclass of IndexedGeometryArray) + * + * @exception IllegalArgumentException if the INTERLEAVED + * bit is set without the BY_REFERENCE bit being set + * + * @exception IllegalArgumentException if the USE_NIO_BUFFER + * bit is set without the BY_REFERENCE bit being set + * + * @exception IllegalArgumentException if the INTERLEAVED + * bit and the VERTEX_ATTRIBUTES bit are both set + * + * @exception IllegalArgumentException if the + * BY_REFERENCE_INDICES + * bit is set without the BY_REFERENCE and + * USE_COORD_INDEX_ONLY bits being set + * + * @exception IllegalArgumentException if + * texCoordSetCount < 0 + * + * @exception IllegalArgumentException if any element in + * texCoordSetMap[] >= texCoordSetCount. + * + * @exception IllegalArgumentException if + * vertexAttrCount > 0 and the + * VERTEX_ATTRIBUTES bit is not set + * + * @exception IllegalArgumentException if + * vertexAttrCount < 0 + * + * @exception IllegalArgumentException if + * vertexAttrSizes.length != vertexAttrCount + * + * @exception IllegalArgumentException if any element in + * vertexAttrSizes[] is < 1 or + * > 4. + * + * @since Java 3D 1.4 + */ + public GeometryArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes) { + + if (vertexCount < 0) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96")); + + if (texCoordSetCount < 0) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray124")); + + if (vertexAttrCount < 0) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray125")); + + 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 & INTERLEAVED) != 0 && + (vertexFormat & VERTEX_ATTRIBUTES) != 0) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray128")); + } + + if ((vertexFormat & USE_COORD_INDEX_ONLY) != 0 && + !(this instanceof IndexedGeometryArray)) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray135")); + } + + //NVaidya + if ((vertexFormat & BY_REFERENCE_INDICES) != 0) { + if (!(this instanceof IndexedGeometryArray)) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray136")); + if ((vertexFormat & BY_REFERENCE) == 0) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray137")); + if ((vertexFormat & USE_COORD_INDEX_ONLY) == 0) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray138")); + } + + 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]; + } + } + + if ((vertexFormat & VERTEX_ATTRIBUTES) != 0) { + if (vertexAttrCount > 0) { + if (vertexAttrCount != vertexAttrSizes.length) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray132")); + } + + for (int i = 0; i < vertexAttrSizes.length; i++) { + if (vertexAttrSizes[i] < 1 || vertexAttrSizes[i] > 4) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray133")); + } + } + } else { + if (vertexAttrSizes != null && vertexAttrSizes.length != 0) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray132")); + } + } + } else { + if (vertexAttrCount > 0) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray131")); + } + } + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((GeometryArrayRetained)this.retained).createGeometryArrayData( + vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + } + + + //------------------------------------------------------------------ + // 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); + } + + + /** + * Retrieves the number of vertex attributes in this GeometryArray + * object. + * + * @return the number of vertex attributes in this GeometryArray + * object + * + * @since Java 3D 1.4 + */ + public int getVertexAttrCount() { + return ((GeometryArrayRetained)this.retained).getVertexAttrCount(); + } + + + /** + * Retrieves the vertex attribute sizes array from this + * GeometryArray object. + * + * @param vertexAttrSizes an array that will receive a copy of + * the vertex attribute sizes array. The array must hold at least + * vertexAttrCount elements. + * + * @since Java 3D 1.4 + */ + public void getVertexAttrSizes(int[] vertexAttrSizes) { + ((GeometryArrayRetained)this.retained).getVertexAttrSizes(vertexAttrSizes); + } + + + /** + * 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. + *

+ * 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 BY_REFERENCE, 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 vertexCount. + * + * @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 + *

+ * @exception IllegalArgumentException if any of the following are + * true: + *

    + * validVertexCount < 0,
    + * initialVertexIndex + validVertexCount > vertexCount,
    + * initialCoordIndex + validVertexCount > vertexCount,
    + * initialColorIndex + validVertexCount > vertexCount,
    + * initialNormalIndex + validVertexCount > vertexCount,
    + * initialTexCoordIndex + validVertexCount > vertexCount,
    + * initialVertexAttrIndex + validVertexCount > vertexCount + *
+ *

+ * @exception ArrayIndexOutOfBoundsException if the geometry data format + * is BY_REFERENCE and any the following + * are true for non-null array references: + *

    + * CoordRef.length < num_words * + * (initialCoordIndex + validVertexCount),
    + * ColorRef.length < num_words * + * (initialColorIndex + validVertexCount),
    + * NormalRef.length < num_words * + * (initialNormalIndex + validVertexCount),
    + * TexCoordRef.length < num_words * + * (initialTexCoordIndex + validVertexCount),
    + * VertexAttrRef.length < num_words * + * (initialVertexAttrIndex + validVertexCount),
    + * InterleavedVertices.length < words_per_vertex * + * (initialVertexIndex + validVertexCount)
    + *
+ * where num_words depends on which variant of + * setArrayRef is used, and + * words_per_vertex 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 originalNodeComponent + * into the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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(); + int vAttrCount = src.getVertexAttrCount(); + for (int i=0; i < setCount; i++) { + dst.setInitialTexCoordIndex(i, src.getInitialTexCoordIndex(i)); + } + if ((format & INTERLEAVED) == 0) { + if ((format & USE_NIO_BUFFER) == 0) { + // Java arrays + 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()); + + switch (src.getVertexAttrType()) { + case GeometryArrayRetained.AF: + for (int i=0; i < vAttrCount; i++) { + dst.setVertexAttrRefFloat(i, src.getVertexAttrRefFloat(i)); + } + break; + } + + switch (src.getTexCoordType()) { + case GeometryArrayRetained.TF: + for (int i=0; i < setCount; i++) { + dst.setTexCoordRefFloat(i, src.getTexCoordRefFloat(i)); + } + break; + case GeometryArrayRetained.T2F: + for (int i=0; i < setCount; i++) { + dst.setTexCoordRef2f(i, src.getTexCoordRef2f(i)); + } + break; + case GeometryArrayRetained.T3F: + for (int i=0; i < setCount; i++) { + dst.setTexCoordRef3f(i, src.getTexCoordRef3f(i)); + } + break; + } + } else { + // NIO buffer + dst.setCoordRefBuffer(src.getCoordRefBuffer()); + dst.setColorRefBuffer(src.getColorRefBuffer()); + dst.setNormalRefBuffer(src.getNormalRefBuffer()); + + switch (src.getVertexAttrType()) { + case GeometryArrayRetained.AF: + for (int i=0; i < vAttrCount; i++) { + dst.setVertexAttrRefBuffer(i, src.getVertexAttrRefBuffer(i)); + } + break; + } + + switch (src.getTexCoordType()) { + case GeometryArrayRetained.TF: + for (int i=0; i < setCount; i++) { + dst.setTexCoordRefBuffer(i, src.getTexCoordRefBuffer(i)); + } + break; + } + } + } 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 BY_REFERENCE + * or when the data mode is INTERLEAVED. + * + * @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: + *
    + * initialVertexIndex < 0 or
    + * initialVertexIndex + validVertexCount > vertexCount
    + *
+ * + * @exception ArrayIndexOutOfBoundsException if the geometry data format + * is INTERLEAVED, the InterleavedVertices array is + * non-null, and: + *
    + * InterleavedVertices.length < num_words * + * (initialVertexIndex + validVertexCount)
    + *
+ * where num_words depends on which vertex formats are enabled. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE and is not + * INTERLEAVED. + * + * @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 not BY_REFERENCE + * or when the data mode is INTERLEAVED. + * @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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 start for length 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 coordinates 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 BY_REFERENCE. + */ + 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 start for length 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 coordinates 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 BY_REFERENCE. + */ + 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 start for length 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 coordinates 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 BY_REFERENCE. + */ + 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 start for length 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 coordinates 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 BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 colors + * starting at index start for length 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 colors 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 colors + * starting at index start for length 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 colors 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 colors + * starting at index start for length 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 colors 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 colors + * starting at index start for length 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 colors 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 colors + * starting at index start for length 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 colors 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 colors + * starting at index start for length 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 colors 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * @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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 normals + * starting at index start and ending at index start+length. + * @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 normals 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 normals + * starting at index start and ending at index start+length. + * @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 normals 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 vertexFormat or array index for element is out of bounds. + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + */ + 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 + * setTextureCoordinate(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinate(int texCoordSet, TexCoord2f texCoord) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinate(int texCoordSet, TexCoord3f texCoord) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinates(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinates(int texCoordSet, TexCoord2f texCoords[]) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinates(int texCoordSet, TexCoord3f texCoords[]) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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_4)) != 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinates(int texCoordSet, ...) + */ + 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 + * texCoords starting at index start and + * ending at index start+length. + * + * @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 texCoords + * 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinates(int texCoordSet, TexCoord2f texCoords[], ...) + */ + 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 + * texCoords starting at index start and + * ending at index start+length. + * + * @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 texCoords + * 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * setTextureCoordinates(int texCoordSet, TexCoord3f texCoords[], ...) + */ + 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 + * start and ending at index start+length. + * + * @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 texCoords + * 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * start and ending at index start+length. + * + * @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 texCoords + * 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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); + } + + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr source array of 1, 2, 3 or 4 values containing + * the new vertex attribute + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range, or if the vertexAttr array is + * too small. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @since Java 3D 1.4 + */ + public void setVertexAttr(int vertexAttrNum, int index, + float[] vertexAttr) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttr, 0, 1); + } + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr the Point2f containing the new vertex attribute + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 2. + * + * @since Java 3D 1.4 + */ + public void setVertexAttr(int vertexAttrNum, int index, + Point2f vertexAttr) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 2) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr the Point3f containing the new vertex attribute + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 3. + * + * @since Java 3D 1.4 + */ + public void setVertexAttr(int vertexAttrNum, int index, + Point3f vertexAttr) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 3) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr the Point4f containing the new vertex attribute + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 4. + * + * @since Java 3D 1.4 + */ + public void setVertexAttr(int vertexAttrNum, int index, + Point4f vertexAttr) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 4) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Sets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The entire source array is copied to this + * geometry array. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of 1*n, 2*n, 3*n, or 4*n values + * containing n new vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range, or if the vertexAttr array is + * too large. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + float[] vertexAttrs) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length / size); + } + + /** + * Sets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The entire source array is copied to this + * geometry array. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point2f objects containing new + * vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range, or if the vertexAttr array is + * too large. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 2. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + Point2f[] vertexAttrs) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 2) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length); + } + + /** + * Sets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The entire source array is copied to this + * geometry array. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point3f objects containing new + * vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range, or if the vertexAttr array is + * too large. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 3. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + Point3f[] vertexAttrs) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 3) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length); + } + + /** + * Sets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The entire source array is copied to this + * geometry array. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point4f objects containing new + * vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range, or if the vertexAttr array is + * too large. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 4. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + Point4f[] vertexAttrs) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 4) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length); + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of 1*n, 2*n, 3*n, or 4*n values + * containing n new vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes 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 any of index, + * (index+length), or vertexAttrNum are out of range, or if + * vertexAttrs is too small. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + float[] vertexAttrs, + int start, int length) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, start, length); + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point2f objects containing new + * vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes 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 any of index, + * (index+length), or vertexAttrNum are out of range, or if + * vertexAttrs is too small. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 2. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + Point2f[] vertexAttrs, + int start, int length) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 2) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, start, length); + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point3f objects containing new + * vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes 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 any of index, + * (index+length), or vertexAttrNum are out of range, or if + * vertexAttrs is too small. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 3. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + Point3f[] vertexAttrs, + int start, int length) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 3) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, start, length); + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point4f objects containing new + * vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes 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 any of index, + * (index+length), or vertexAttrNum are out of range, or if + * vertexAttrs is too small. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 4. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrs(int vertexAttrNum, int index, + Point4f[] vertexAttrs, + int start, int length) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 4) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).setVertexAttrs( + vertexAttrNum, index, vertexAttrs, start, length); + } + + + /** + * Gets the coordinate associated with the vertex at + * the specified index for this object using data in texCoords + * @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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + * @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 BY_REFERENCE. + * @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 BY_REFERENCE. + * @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 BY_REFERENCE. + * @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 vertexCount-index 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + * @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 vertexCount-index 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 BY_REFERENCE. + * @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 vertexCount-index 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 BY_REFERENCE. + * @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 vertexCount-index 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 BY_REFERENCE. + * @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 BY_REFERENCE. + */ + 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 vertexCount-index 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 BY_REFERENCE. + */ + 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 + * getTextureCoordinate(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * getTextureCoordinate(int texCoordSet, TexCoord2f texCoord) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * getTextureCoordinate(int texCoordSet, TexCoord3f texCoord) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * getTextureCoordinates(int texCoordSet, ...) + */ + 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 vertexCount-index 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * getTextureCoordinates(int texCoordSet, TexCoord2f texCoords[]) + */ + 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 vertexCount-index 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 + * getTextureCoordinates(int texCoordSet, TexCoord3f texCoords[]) + */ + 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 vertexCount-index 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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 vertexCount-index 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @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); + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index source vertex index in this geometry array + * @param vertexAttr array of 1, 2, 3 or 4 values that will receive the + * vertex attribute + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range, or if the vertexAttr array is + * too small. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @since Java 3D 1.4 + */ + public void getVertexAttr(int vertexAttrNum, int index, + float[] vertexAttr) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index source vertex index in this geometry array + * @param vertexAttr the vector that will receive the vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 2. + * + * @since Java 3D 1.4 + */ + public void getVertexAttr(int vertexAttrNum, int index, + Point2f vertexAttr) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 2) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index source vertex index in this geometry array + * @param vertexAttr the vector that will receive the vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 3. + * + * @since Java 3D 1.4 + */ + public void getVertexAttr(int vertexAttrNum, int index, + Point3f vertexAttr) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 3) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index source vertex index in this geometry array + * @param vertexAttr the vector that will receive the vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 4. + * + * @since Java 3D 1.4 + */ + public void getVertexAttr(int vertexAttrNum, int index, + Point4f vertexAttr) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 4) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttr( + vertexAttrNum, index, vertexAttr); + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The length of the destination + * array determines the number of vertex attributes copied. + * A maximum of vertexCount-index vertex attributes + * are copied. If the destination array is larger than is needed + * to hold the vertex attributes, the excess locations in the + * array are not modified. If the destination array is smaller + * than is needed to hold the vertex attributes, only as + * many vertex attributes as the array will hold are copied. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting source vertex index in this geometry array + * @param vertexAttrs destination array of 1*n, 2*n, 3*n, or 4*n values + * that will receive n new vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @since Java 3D 1.4 + */ + public void getVertexAttrs(int vertexAttrNum, int index, + float[] vertexAttrs) { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttrs( + vertexAttrNum, index, vertexAttrs); + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The length of the destination + * array determines the number of vertex attributes copied. + * A maximum of vertexCount-index vertex attributes + * are copied. If the destination array is larger than is needed + * to hold the vertex attributes, the excess locations in the + * array are not modified. If the destination array is smaller + * than is needed to hold the vertex attributes, only as + * many vertex attributes as the array will hold are copied. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting source vertex index in this geometry array + * @param vertexAttrs destination array of Point2f objects that will + * receive the vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 2. + * + * @since Java 3D 1.4 + */ + public void getVertexAttrs(int vertexAttrNum, int index, + Point2f[] vertexAttrs) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 2) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttrs( + vertexAttrNum, index, vertexAttrs); + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The length of the destination + * array determines the number of vertex attributes copied. + * A maximum of vertexCount-index vertex attributes + * are copied. If the destination array is larger than is needed + * to hold the vertex attributes, the excess locations in the + * array are not modified. If the destination array is smaller + * than is needed to hold the vertex attributes, only as + * many vertex attributes as the array will hold are copied. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting source vertex index in this geometry array + * @param vertexAttrs destination array of Point3f objects that will + * receive the vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 3. + * + * @since Java 3D 1.4 + */ + public void getVertexAttrs(int vertexAttrNum, int index, + Point3f[] vertexAttrs) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 3) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttrs( + vertexAttrNum, index, vertexAttrs); + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. The length of the destination + * array determines the number of vertex attributes copied. + * A maximum of vertexCount-index vertex attributes + * are copied. If the destination array is larger than is needed + * to hold the vertex attributes, the excess locations in the + * array are not modified. If the destination array is smaller + * than is needed to hold the vertex attributes, only as + * many vertex attributes as the array will hold are copied. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting source vertex index in this geometry array + * @param vertexAttrs destination array of Point4f objects that will + * receive the vertex attributes + * + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this object is part of a live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE. + * + * @exception IllegalStateException if the size of the specified + * vertex attribute number is not 4. + * + * @since Java 3D 1.4 + */ + public void getVertexAttrs(int vertexAttrNum, int index, + Point4f[] vertexAttrs) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127")); + } + } + + int format = ((GeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE) != 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray82")); + } + + int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum]; + if (size != 4) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray134")); + } + + ((GeometryArrayRetained)this.retained).getVertexAttrs( + vertexAttrNum, index, vertexAttrs); + } + + + //------------------------------------------------------------------ + // 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 BY_REFERENCE + * and is not INTERLEAVED. + * + * @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 + *

+ * @exception IllegalStateException if the data mode for this geometry + * array object is not BY_REFERENCE or if the data mode + * is INTERLEAVED. + *

+ * @exception IllegalArgumentException if either of the following are + * true: + *

    + * initialCoordIndex < 0 or
    + * initialCoordIndex + validVertexCount > vertexCount
    + *
+ *

+ * @exception ArrayIndexOutOfBoundsException if + * the CoordRef array is non-null and: + *

    + * CoordRef.length < num_words * + * (initialCoordIndex + validVertexCount)
    + *
+ * where num_words depends on which variant of + * setCoordRef 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 BY_REFERENCE + * and is not INTERLEAVED. + * @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 BY_REFERENCE + * and is not INTERLEAVED. + * + * @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 + *

+ * @exception IllegalStateException if the data mode for this geometry + * array object is not BY_REFERENCE or if the data mode + * is INTERLEAVED. + *

+ * @exception IllegalArgumentException if either of the following are + * true: + *

    + * initialColorIndex < 0 or
    + * initialColorIndex + validVertexCount > vertexCount
    + *
+ *

+ * @exception ArrayIndexOutOfBoundsException if + * the ColorRef array is non-null and: + *

    + * ColorRef.length < num_words * + * (initialColorIndex + validVertexCount)
    + *
+ * where num_words depends on which variant of + * setColorRef 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 BY_REFERENCE + * and is not INTERLEAVED. + * @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 BY_REFERENCE + * and is not INTERLEAVED. + * + * @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 + *

+ * @exception IllegalStateException if the data mode for this geometry + * array object is not BY_REFERENCE or if the data mode + * is INTERLEAVED. + *

+ * @exception IllegalArgumentException if either of the following are + * true: + *

    + * initialNormalIndex < 0 or
    + * initialNormalIndex + validVertexCount > vertexCount
    + *
+ *

+ * @exception ArrayIndexOutOfBoundsException if normals + * the NormalRef array is non-null and: + *

    + * NormalRef.length < num_words * + * (initialNormalIndex + validVertexCount)
    + *
+ * where num_words depends on which variant of + * setNormalRef 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 BY_REFERENCE + * and is not INTERLEAVED. + * @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 BY_REFERENCE + * and is not INTERLEAVED. + * + * @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 + *

+ * @exception IllegalStateException if the data mode for this geometry + * array object is not BY_REFERENCE or if the data mode + * is INTERLEAVED. + *

+ * @exception IllegalArgumentException if either of the following are + * true: + *

    + * initialTexCoordIndex < 0 or
    + * initialTexCoordIndex + validVertexCount > vertexCount
    + *
+ *

+ * @exception ArrayIndexOutOfBoundsException if + * the TexCoordRef array is non-null and: + *

    + * TexCoordRef.length < num_words * + * (initialTexCoordIndex + validVertexCount)
    + *
+ * where num_words depends on which variant of + * setTexCoordRef is used. + *

+ * @exception ArrayIndexOutOfBoundsException if none of the + * TEXTURE_COORDINATE bits are set in the + * vertexFormat 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 BY_REFERENCE + * and is not INTERLEAVED. + * + * @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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat 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 initial vertex attribute index for the specified + * vertex attribute number for this GeometryArray object. This + * index specifies the first vertex attribute within the array + * of vertex attributes 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 BY_REFERENCE + * and is not INTERLEAVED. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param initialVertexAttrIndex the new initial vertex attribute index. + * + * @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 BY_REFERENCE or if the data mode + * is INTERLEAVED. + *

+ * @exception IllegalArgumentException if either of the following are + * true: + *

    + * initialVertexAttrIndex < 0 or
    + * initialVertexAttrIndex + validVertexCount > vertexCount
    + *
+ *

+ * @exception ArrayIndexOutOfBoundsException if + * the VertexAttrRef array is non-null and: + *

    + * VertexAttrRef.length < num_words * + * (initialVertexAttrIndex + validVertexCount)
    + *
+ * where num_words is the size of the specified + * vertexAttrNum (1, 2, 3, or 4). + *

+ * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is + * out of range. + * + * @since Java 3D 1.4 + */ + public void setInitialVertexAttrIndex(int vertexAttrNum, + int initialVertexAttrIndex) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_COUNT_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90")); + } + } + + if (initialVertexAttrIndex < 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).setInitialVertexAttrIndex( + vertexAttrNum, initialVertexAttrIndex); + + // NOTE: the check for initialVertexAttrIndex + validVertexCount > + // vertexCount needs to be done in the retained method + } + + + /** + * Gets the initial vertex attribute index for the specified + * vertex attribute number for this GeometryArray object. + * This attribute is only used when the data mode for this + * geometry array object is BY_REFERENCE + * and is not INTERLEAVED. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * + * @return the current initial vertex attribute index for the specified + * vertex attribute number + * + * @exception ArrayIndexOutOfBoundsException if vertexAttrNum 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.4 + */ + public int getInitialVertexAttrIndex(int vertexAttrNum) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_COUNT_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91")); + } + } + + return ((GeometryArrayRetained)this.retained).getInitialVertexAttrIndex( + vertexAttrNum); + } + + + /** + * 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 x, y, + * and z values for each vertex (for a total of 3*n + * values, where n 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*n 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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 + * coords.getBuffer().limit() < + * 3 * (initialCoordIndex + validVertexCount). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the coordinate index array is greater than or equal to the + * number of vertices defined by the coords object, + * coords.getBuffer().limit() / 3. + * + * @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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 x, y, + * and z values for each vertex (for a total of 3*n + * values, where n is the number of vertices). Only one of + * coordRefFloat, coordRefDouble, + * coordRef3f, or coordRef3d 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*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * @exception IllegalArgumentException if the specified array is + * non-null and any other coordinate reference is also non-null. + * @exception ArrayIndexOutOfBoundsException if + * coords.length < 3 * (initialCoordIndex + validVertexCount). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the coordinate index array is greater than or equal to the + * number of vertices defined by the coords array, + * coords.length / 3. + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 x, y, + * and z values for each vertex (for a total of 3*n + * values, where n is the number of vertices). Only one of + * coordRefFloat, coordRefDouble, + * coordRef3f, or coordRef3d 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*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * @exception IllegalArgumentException if the specified array is + * non-null and any other coordinate reference is also non-null. + * @exception ArrayIndexOutOfBoundsException if + * coords.length < 3 * (initialCoordIndex + validVertexCount). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the coordinate index array is greater than or equal to the + * number of vertices defined by the coords array, + * coords.length / 3. + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 red, green, + * blue, and, optionally, alpha values for each + * vertex (for a total of 3*n or 4*n values, where + * n is the number of vertices). + * If the color buffer reference is null and colors are enabled + * (that is, the vertexFormat includes either COLOR_3 or + * COLOR_4), 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*n or 4*n + * 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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 + * COLOR bits are set in the + * vertexFormat, or if + * colors.getBuffer().limit() < num_words * + * (initialColorIndex + validVertexCount), + * where num_words 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the color index array is greater than or equal to the + * number of vertices defined by the colors object, + * colors.getBuffer().limit() / num_words. + * + * @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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 red, green, + * blue, and, optionally, alpha values for each + * vertex (for a total of 3*n or 4*n values, where + * n is the number of vertices). Only one of + * colorRefFloat, colorRefByte, + * colorRef3f, colorRef4f, + * colorRef3b, or colorRef4b 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 COLOR_3 or + * COLOR_4), 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*n or 4*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * @exception IllegalArgumentException if the specified array is + * non-null and any other color reference is also non-null. + * + * @exception ArrayIndexOutOfBoundsException if none of the + * COLOR bits are set in the + * vertexFormat, or if + * colors.length < num_words * + * (initialColorIndex + validVertexCount), + * where num_words 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the color index array is greater than or equal to the + * number of vertices defined by the colors array, + * colors.length / num_words. + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 red, green, + * blue, and, optionally, alpha values for each + * vertex (for a total of 3*n or 4*n values, where + * n is the number of vertices). Only one of + * colorRefFloat, colorRefByte, + * colorRef3f, colorRef4f, + * colorRef3b, or colorRef4b 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 COLOR_3 or + * COLOR_4), 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*n or 4*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * @exception IllegalArgumentException if the specified array is + * non-null and any other color reference is also non-null. + * + * @exception ArrayIndexOutOfBoundsException if none of the + * COLOR bits are set in the + * vertexFormat, or if + * colors.length < num_words * + * (initialColorIndex + validVertexCount), + * where num_words 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the color index array is greater than or equal to the + * number of vertices defined by the colors array, + * colors.length / num_words. + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 nx, ny, + * and nz values for each vertex (for a total of 3*n + * values, where n is the number of vertices). + * If the normal buffer reference is null and normals are enabled + * (that is, the vertexFormat includes NORMAL), 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*n 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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception IllegalArgumentException if the java.nio.Buffer + * contained in the specified J3DBuffer is not a + * java.nio.FloatBuffer object. + * + * @exception ArrayIndexOutOfBoundsException if + * NORMALS bit is not set in the + * vertexFormat, or if + * normals.getBuffer().limit() < + * 3 * (initialNormalIndex + validVertexCount). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the normal index array is greater than or equal to the + * number of vertices defined by the normals object, + * normals.getBuffer().limit() / 3. + * + * @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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 nx, ny, + * and nz values for each vertex (for a total of 3*n + * values, where n is the number of vertices). Only one of + * normalRefFloat or normalRef3f 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 + * NORMAL), 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*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * @exception IllegalArgumentException if the specified array is + * non-null and any other normal reference is also non-null. + * @exception ArrayIndexOutOfBoundsException if + * NORMALS bit is not set in the + * vertexFormat, or if + * normals.length < 3 * (initialNormalIndex + validVertexCount). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the normal index array is greater than or equal to the + * number of vertices defined by the normals array, + * normals.length / 3. + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 buffer reference for the specified + * texture coordinate set to the + * specified buffer object. The buffer contains a java.nio.FloatBuffer + * object containing s, + * t, and, optionally, r and q values for each + * vertex (for + * a total of 2*n , 3*n or 4*n values, + * where n is + * the number of vertices). + * If the texCoord buffer reference is null and texture + * coordinates are enabled (that is, the vertexFormat includes + * TEXTURE_COORDINATE_2, + * TEXTURE_COORDINATE_3, or + * TEXTURE_COORDINATE_4), 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*n, 3*n or + * 4*n 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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat, or if texCoordSet is out of range, + * or if + * texCoords.getBuffer().limit() < num_words + * * (initialTexCoordIndex + validVertexCount), + * where num_words 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the texture coordinate index array is greater than or equal to the + * number of vertices defined by the texCoords object, + * texCoords.getBuffer().limit() / num_words. + * + * @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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception ArrayIndexOutOfBoundsException if none of the + * TEXTURE_COORDINATE bits are set in the + * vertexFormat 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 s, + * t, and, optionally, r and q values for each + * vertex (for + * a total of 2*n , 3*n or 4*n values, + * where n is + * the number of vertices). Only one of + * texCoordRefFloat, texCoordRef2f, or + * texCoordRef3f 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 + * TEXTURE_COORDINATE_2, + * TEXTURE_COORDINATE_3, or + * TEXTURE_COORDINATE_4), 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*n, 3*n or + * 4*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * @exception IllegalArgumentException if the specified array is + * non-null and any other texCoord reference is also non-null. + * + * @exception ArrayIndexOutOfBoundsException if none of the + * TEXTURE_COORDINATE bits are set in the + * vertexFormat, or if texCoordSet is out of range, + * or if + * texCoords.length < num_words * + * (initialTexCoordIndex + validVertexCount), + * where num_words 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the texture coordinate index array is greater than or equal to the + * number of vertices defined by the texCoords array, + * texCoords.length / num_words. + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception ArrayIndexOutOfBoundsException if none of the + * TEXTURE_COORDINATE bits are set in the + * vertexFormat 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 vertex attribute buffer reference for the specified + * vertex attribute number to the specified buffer object. The + * buffer contains a java.nio.FloatBuffer object containing 1, 2, + * 3, or 4 values for each vertex (for a total of 1*n, + * 2*n, 3*n, or 4*n values, where n is + * the number of vertices). + * If the vertexAttr buffer reference is null and vertex + * attributes are enabled (that is, the vertexFormat includes + * VERTEX_ATTRIBUTES), the entire geometry array + * object is treated as if it were null--any Shape3D node that + * uses this geometry array will not be drawn. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * + * @param vertexAttrs a J3DBuffer object to which a reference will + * be set. The buffer contains an NIO buffer of 1*n, + * 2*n, 3*n, or 4*n 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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception IllegalArgumentException if the java.nio.Buffer + * contained in the specified J3DBuffer is not a + * java.nio.FloatBuffer object. + * + * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is out of + * range, or if + * vertexAttrs.getBuffer().limit() < num_words + * * (initialVertexAttrIndex + validVertexCount), + * where num_words is the size of the specified + * vertexAttrNum (1, 2, 3, or 4). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the vertex attribute index array is greater than or equal to the + * number of vertices defined by the vertexAttrs object, + * vertexAttrs.getBuffer().limit() / num_words. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrRefBuffer(int vertexAttrNum, J3DBuffer vertexAttrs) { + 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).setVertexAttrRefBuffer( + vertexAttrNum, vertexAttrs); + } + + + /** + * Gets the vertex attribute array buffer reference for the specified + * vertex attribute number. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * + * @return the current vertex attribute array buffer reference + * for the specified vertex attribute number + * + * @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 BY_REFERENCE, + * is not USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is out + * of range. + * + * @since Java 3D 1.4 + */ + public J3DBuffer getVertexAttrRefBuffer(int vertexAttrNum) { + 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).getVertexAttrRefBuffer(vertexAttrNum); + } + + + /* + * XXXX: add the following to the javadoc if we ever add double-precision + * methods for vertex attribtues. + * + *----------------------------------------------------------------- + * Only one of vertexAttrRefFloat, or + * vertexAttrRefDouble 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 vertexAttr array references are null and vertex + * ... + * @exception IllegalArgumentException if the specified array is + * non-null and any other vertexAttr reference is also non-null. + * ... + *----------------------------------------------------------------- + */ + + /** + * Sets the float vertex attribute array reference for the + * specified vertex attribute number to the specified array. The + * array contains 1, 2, 3, or 4 floating-point values for each + * vertex (for a total of 1*n, 2*n, 3*n, or + * 4*n values, where n is the number of vertices). + * + * If the vertexAttr array reference is null and vertex + * attributes are enabled (that is, the vertexFormat includes + * VERTEX_ATTRIBUTES), the entire geometry array + * object is treated as if it were null--any Shape3D node that + * uses this geometry array will not be drawn. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * + * @param vertexAttrs an array of 1*n, 2*n, + * 3*n, or 4*n 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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is + * out of range, or if + * vertexAttrs.length < num_words * + * (initialVertexAttrIndex + validVertexCount), + * where num_words is the size of the specified + * vertexAttrNum (1, 2, 3, or 4). + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * in the vertex attribute index array is greater than or equal to the + * number of vertices defined by the vertexAttrs array, + * vertexAttrs.length / num_words. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrRefFloat(int vertexAttrNum, float[] vertexAttrs) { + + 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).setVertexAttrRefFloat( + vertexAttrNum, vertexAttrs); + + // NOTE: the checks for multiple non-null references, and the + // array length check need to be done in the retained method + } + + + /** + * Gets the float vertex attribute array reference for the specified + * vertex attribute number. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * + * @return the current float vertex attribute array reference + * for the specified vertex attribute number + * + * @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 BY_REFERENCE, + * is USE_NIO_BUFFER, or is INTERLEAVED. + * + * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is + * out of range. + * + * @since Java 3D 1.4 + */ + public float[] getVertexAttrRefFloat(int vertexAttrNum) { + + 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).getVertexAttrRefFloat( + vertexAttrNum); + } + + + /** + * 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. + * Vertex attributes are not supported in interleaved mode. + * 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 + * TEXTURE_COORDINATE_2, 3 words per texture + * coordinate set per vertex for + * TEXTURE_COORDINATE_3 or 4 words per texture + * coordinate set per vertex for + * TEXTURE_COORDINATE_4. Colors, if enabled, use 3 + * words per vertex for COLOR_3 or 4 words per vertex + * for COLOR_4. 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 + * COORDINATES, COLOR_3, and + * NORMALS would be: red, green, + * blue, Nx, Ny, Nz, x, + * y, z. 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 + * words_per_vertex in the array. The total number of + * words needed to store n vertices is + * words_per_vertex*n. + * + * @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 INTERLEAVED + * or is USE_NIO_BUFFER. + * + * @exception ArrayIndexOutOfBoundsException if + * vertexData.length < words_per_vertex * + * (initialVertexIndex + validVertexCount), + * where words_per_vertex depends on which formats are enabled. + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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, + * vertexData.length / words_per_vertex. + * + * @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 INTERLEAVED + * or is USE_NIO_BUFFER. + * + * @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. + * Vertex attributes are not supported in interleaved mode. + * 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 + * TEXTURE_COORDINATE_2, 3 words per texture + * coordinate set per vertex for + * TEXTURE_COORDINATE_3 or 4 words per texture + * coordinate set per vertex for + * TEXTURE_COORDINATE_4. Colors, if enabled, use 3 + * words per vertex for COLOR_3 or 4 words per vertex + * for COLOR_4. 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 + * COORDINATES, COLOR_3, and + * NORMALS would be: red, green, + * blue, Nx, Ny, Nz, x, + * y, z. 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 + * words_per_vertex in the buffer. The total number of + * words needed to store n vertices is + * words_per_vertex*n. + * + * @param vertexData a J3DBuffer object to which a reference will be set. + * The buffer contains an NIO float buffer of + * words_per_vertex*n 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 INTERLEAVED + * or is not USE_NIO_BUFFER. + * + * @exception IllegalArgumentException if the java.nio.Buffer + * contained in the specified J3DBuffer is not a + * java.nio.FloatBuffer object. + * + * @exception ArrayIndexOutOfBoundsException if + * vertexData.getBuffer().limit() < words_per_vertex * + * (initialVertexIndex + validVertexCount), + * where words_per_vertex depends on which formats are enabled. + * + * @exception ArrayIndexOutOfBoundsException if this GeometryArray + * object is a subclass of IndexedGeometryArray, and any element + * in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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, + * vertexData.getBuffer().limit() / words_per_vertex. + * + * @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 INTERLEAVED + * or is not USE_NIO_BUFFER. + * + * @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/javax/media/j3d/GeometryArrayRetained.java b/src/javax/media/j3d/GeometryArrayRetained.java new file mode 100644 index 0000000..53a691a --- /dev/null +++ b/src/javax/media/j3d/GeometryArrayRetained.java @@ -0,0 +1,11213 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + +import javax.vecmath.Color3b; +import javax.vecmath.Color3f; +import javax.vecmath.Color4b; +import javax.vecmath.Color4f; +import javax.vecmath.Point2d; +import javax.vecmath.Point2f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Point4d; +import javax.vecmath.Point4f; +import javax.vecmath.TexCoord2f; +import javax.vecmath.TexCoord3f; +import javax.vecmath.TexCoord4f; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector3f; + + +/** + * 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{ + + // XXXX: 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 + private 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; + + // Offset (in words) within each vertex of each vertex attribute + int[] vertexAttrOffsets; + + // Stride (size) of all vertex attributes + int vertexAttrStride; + + // alpha value for transparency and texture blending + private float[] lastAlpha = new float[1]; + float lastScreenAlpha = -1; + + int colorChanged = 0; + + // 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; + FloatBuffer floatBufferRefCoords = null; + DoubleBuffer doubleBufferRefCoords = null; + + // Initial index to use for rendering + int initialCoordIndex = 0; + int initialColorIndex = 0; + int initialNormalIndex = 0; + int[] initialTexCoordIndex = null; + int[] initialVertexAttrIndex = 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; + FloatBuffer floatBufferRefColors = null; + ByteBuffer 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; + + static final int AF = 0x8000; + static final int VATTR_DEFINED = AF; + + // Flag word indicating the type of by-ref texCoord. We will copy this to + // the vertexType field only when the references for all texture coordinate + // sets are set to non-null values. + private int texCoordType = 0; + + // Flag word indicating the type of by-ref vertex attr. We will copy this to + // the vertexType field only when the references for all vertex attrs + // are set to non-null values. + private int vertexAttrType = 0; + + // 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; + static final int VATTR_FLOAT = 0x40; + + + // used by "by reference" normals + float[] floatRefNormals = null; + Vector3f[] v3fRefNormals = null; + + // Used for NIO buffer normals + J3DBuffer normalRefBuffer = null; + FloatBuffer floatBufferRefNormals = null; + + // used for "by reference" vertex attrs + float[][] floatRefVertexAttrs = null; + + // Used for NIO buffer vertex attrs + J3DBuffer[] vertexAttrsRefBuffer = null; + FloatBuffer[] floatBufferRefVertexAttrs = null; + + // used by "by reference" tex coords + Object[] refTexCoords = null; + TexCoord2f[] t2fRefTexCoords = null; + TexCoord3f[] t3fRefTexCoords = null; + + // Used for NIO buffer tex coords + J3DBuffer[] refTexCoordsBuffer = null; + //FloatBufferWrapper[] floatBufferRefTexCoords = null; + + + // used by interleaved array + float[] interLeavedVertexData = null; + + // used by interleaved NIO buffer + J3DBuffer interleavedVertexBuffer = null; + FloatBuffer interleavedFloatBufferImpl = null; + + // pointers used, when transparency is turned on + // or when its an object such as C3F, P3F etc .. + float[] mirrorFloatRefCoords = null; + double[] mirrorDoubleRefCoords = null; + float[] mirrorFloatRefNormals = null; + float[][] mirrorFloatRefVertexAttrs = null; + float[] mirrorFloatRefTexCoords = null; + Object[] mirrorRefTexCoords = null; + + float[][] mirrorFloatRefColors = new float[1][]; + byte[][] mirrorUnsignedByteRefColors= new byte[1][]; + float[][] mirrorInterleavedColorPointer = null; + + // boolean to determine if a mirror was allocated + int mirrorVertexAllocated = 0; + int mirrorColorAllocated = 0; + 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 VATTR_CHANGED = 0x80; + static final int VERTEX_CHANGED = COORDINATE_CHANGED | + NORMAL_CHANGED | + COLOR_CHANGED | + TEXTURE_CHANGED | + VATTR_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; + + // Vertex attribute information + int vertexAttrCount = 0; + int[] vertexAttrSizes = 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. + int dirtyFlag; + + // each bit corresponds to a unique renderer if shared context + // or a unique canvas otherwise + int resourceCreationMask = 0x0; + + // Fix for Issue 5 + // + // Replace the per-canvas reference count with a per-RenderBin set + // of users. The per-RenderBin set of users of this display list + // is defined as a HashMap where: + // + // key = the RenderBin + // value = a set of RenderAtomListInfo objects using this + // geometry array for display list purposes +private HashMap> dlistUsers = null; + + // timestamp used to create display list. This is either + // one per renderer for useSharedCtx, or one per Canvas for non-shared + // ctx + private 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; + + static final double EPS = 1.0e-13; + + GeometryArrayRetained() { + dirtyFlag = INDEX_CHANGED|VERTEX_CHANGED; + lastAlpha[0] = 1.0f; + } + + + @Override + 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.err.println("Geometry - "+this+"refCount = "+this.refCount); + if (this.refCount > 1) { + // Send to rendering attribute structure, + /* + J3dMessage createMessage = new J3dMessage(); + 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 = new J3dMessage(); + 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); + } + } + + } + + @Override + void clearLive(int refCount) { + super.clearLive(refCount); + + if (this.refCount <= 0) { + isShared = false; + } + } + + @Override + void computeBoundingBox() { + + // System.err.println("computeBoundingBox ...."); + + if (boundsDirty && VirtualUniverse.mc.cacheAutoComputedBounds) { + for(ArrayList users : userLists) { + for(Shape3DRetained shape : users) + shape.dirtyBoundsCache(); + } + } + + if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) { + // by copy + computeBoundingBox(initialVertexIndex, vertexData); + + } else if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) { // USE_NIO_BUFFER + //System.err.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.err.println("vertexFormat & GeometryArray.INTERLEAVED"); + computeBoundingBox(initialCoordIndex, interLeavedVertexData); + } else if ((vertexType & PF) != 0) { + //System.err.println("vertexType & PF"); + computeBoundingBox(floatRefCoords); + } else if ((vertexType & P3F) != 0) { + //System.err.println("vertexType & P3F"); + computeBoundingBox(p3fRefCoords); + } else if ((vertexType & P3D) != 0) { + //System.err.println("vertexType & P3D"); + computeBoundingBox(p3dRefCoords); + } else if ((vertexType & PD) != 0) { + //System.err.println("vertexType & PD"); + computeBoundingBox(doubleRefCoords); + } + + } + + + // NullGeometry is true only for byRef case + void processCoordsChanged(boolean nullGeo) { + + /* + System.err.println("processCoordsChanged : nullGeo " + nullGeo); + System.err.println("Before :processCoordsChanged : geoBounds "); + System.err.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; + geoBounds.getCenter(this.centroid); + } + + } + else { + // re-compute centroid if used + synchronized(centroid) { + recompCentroid = true; + } + + synchronized(geoBounds) { + boundsDirty = true; + computeBoundingBox(); + } + + /* + System.err.println("After :processCoordsChanged : geoBounds "); + System.err.println(geoBounds); + */ + } + } + + + void computeBoundingBox(int vIndex, float[] vdata) { + int i, offset; + double xmin, xmax, ymin, ymax, zmin, zmax; + + + //System.err.println("Before : computeBoundingBox : geoBounds "); + // System.err.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 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.err.println("After : computeBoundingBox : geoBounds "); + System.err.println(geoBounds); + */ + } + + // Compute boundingbox for interleaved nio buffer + void computeBoundingBox(int vIndex, FloatBuffer 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 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 nio buffer + void computeBoundingBox( DoubleBuffer 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 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 nio buffer + void computeBoundingBox( FloatBuffer 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 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.err.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 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.err.println("max(" + xmax + ", " + ymax + ", " + zmax + ")"); + geoBounds.setLower(xmin, ymin, zmin); + // System.err.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 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 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 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; + } + + } + + + @Override + 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= 0.0; + /* + System.err.println("updateAlphaInFloatRefColors ** : lastAlpha[screen] " + + lastAlpha[screen]); + + System.err.println("((colorChanged & (1<= 0.0; + /* + System.err.println("updateAlphaInByteRefColors ## : lastAlpha[screen] " + + lastAlpha[screen]); + + System.err.println("((colorChanged & (1< 0) { + for (int i = oldSize; i < screen+1; i++) { + cfData[i] = new float[stride * vertexCount]; + System.arraycopy(cfData[0], 0, cfData[i], 0, + stride * vertexCount); + lastAlpha[i] = lastAlpha[0]; + } + } + + mvertexData = cfData; + + // Issue 113 - since we copied the data from screen 0, we don't need + // to do any further special processing. + } + + assert lastAlpha[screen] >= 0.0; + + if ((colorChanged & (1<= 0.0; + + if ((colorChanged & (1<= 0 implies one pass for one texture unit state + + @Override + void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, + boolean updateAlpha, float alpha, + int screen, + boolean ignoreVertexColors) { + + int cdirty; + boolean useAlpha = false; + Object[] retVal; + + // Check for by-copy case + 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; + } + + Pipeline.getPipeline().execute(cv.ctx, + this, geoType, isNonUniformScale, + useAlpha, + ignoreVertexColors, + initialVertexIndex, + validVertexCount, + ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + cv.numActiveTexUnit, + vertexAttrCount, vertexAttrSizes, + vdata, null, + 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; + } + + Pipeline.getPipeline().execute(cv.ctx, + this, geoType, isNonUniformScale, + useAlpha, + ignoreVertexColors, + initialVertexIndex, + validVertexCount, + vertexFormat, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + cv.numActiveTexUnit, + vertexAttrCount, vertexAttrSizes, + interLeavedVertexData, cdata, + 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.VERTEX_ATTRIBUTES) != 0) && + (vertexType & VATTR_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 & VATTR_DEFINED) != 0) + vdefined |= VATTR_FLOAT; + if((vertexType & TEXCOORD_DEFINED) != 0) + vdefined |= TEXCOORD_FLOAT; + + Pipeline.getPipeline().executeVA(cv.ctx, + this, geoType, isNonUniformScale, + ignoreVertexColors, + validVertexCount, + (vertexFormat | c4fAllocated), + vdefined, + initialCoordIndex, + mirrorFloatRefCoords, mirrorDoubleRefCoords, + initialColorIndex, cfdata, cbdata, + initialNormalIndex, mirrorFloatRefNormals, + vertexAttrCount, vertexAttrSizes, + initialVertexAttrIndex, mirrorFloatRefVertexAttrs, + ((texCoordSetMap == null) ? 0:texCoordSetMap.length), + texCoordSetMap, + cv.numActiveTexUnit, + 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 + // XXXX: 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 { + // XXXX: to handle alpha case + cdata = null; + // if transparency switch between on/off + if (lastScreenAlpha != -1) { + lastScreenAlpha = -1; + cdirty |= COLOR_CHANGED; + } + } + dirtyFlag = 0; + } + + Pipeline.getPipeline().executeInterleavedBuffer(cv.ctx, + this, geoType, isNonUniformScale, + useAlpha, + ignoreVertexColors, + initialVertexIndex, + validVertexCount, + vertexFormat, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + cv.numActiveTexUnit, + interleavedFloatBufferImpl, cdata, + 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.VERTEX_ATTRIBUTES) != 0) && + (vertexType & VATTR_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 { + // XXXX: 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 { + // XXXX: 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; + } + + Buffer vcoord = null; + Buffer cdataBuffer = null; + FloatBuffer normal=null; + + int vdefined = 0; + if((vertexType & PF) != 0) { + vdefined |= COORD_FLOAT; + vcoord = floatBufferRefCoords; + } else if((vertexType & PD ) != 0) { + vdefined |= COORD_DOUBLE; + vcoord = doubleBufferRefCoords; + } + + if((vertexType & CF ) != 0) { + vdefined |= COLOR_FLOAT; + cdataBuffer = floatBufferRefColors; + } else if((vertexType & CUB) != 0) { + vdefined |= COLOR_BYTE; + cdataBuffer = byteBufferRefColors; + } + + if((vertexType & NORMAL_DEFINED) != 0) { + vdefined |= NORMAL_FLOAT; + normal = floatBufferRefNormals; + } + + if ((vertexType & VATTR_DEFINED) != 0) { + vdefined |= VATTR_FLOAT; + } + + if((vertexType & TEXCOORD_DEFINED) != 0) + vdefined |= TEXCOORD_FLOAT; + + Pipeline.getPipeline().executeVABuffer(cv.ctx, + this, geoType, isNonUniformScale, + ignoreVertexColors, + validVertexCount, + (vertexFormat | c4fAllocated), + vdefined, + initialCoordIndex, + vcoord, + initialColorIndex, + cdataBuffer, + cfdata, cbdata, + initialNormalIndex, + normal, + vertexAttrCount, vertexAttrSizes, + initialVertexAttrIndex, + floatBufferRefVertexAttrs, + ((texCoordSetMap == null) ? 0:texCoordSetMap.length), + texCoordSetMap, + cv.numActiveTexUnit, + initialTexCoordIndex,texCoordStride, + refTexCoords, cdirty); + }// end of all vertex data being set + }// end of non interleaved case + }// end of by reference with nio-buffer case + } + + void buildGA(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, + boolean updateAlpha, float alpha, boolean ignoreVertexColors, + Transform3D xform, Transform3D nxform) { + + float[] vdata = null; + + // NIO buffers are no longer supported in display lists + assert (vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0; + + 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.err.println("calling native buildGA()"); + System.err.println("geoType = "+geoType+" initialVertexIndex = "+initialVertexIndex+" validVertexCount = "+validVertexCount+" vertexFormat = "+vertexFormat+" vertexData = "+vertexData); + */ + Pipeline.getPipeline().buildGA(cv.ctx, + this, geoType, isNonUniformScale, + updateAlpha, alpha, ignoreVertexColors, + initialVertexIndex, + validVertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + vertexAttrCount, vertexAttrSizes, + (xform == null) ? null : xform.mat, + (nxform == null) ? null : nxform.mat, + vdata); + } + 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.VERTEX_ATTRIBUTES) != 0) && + (vertexType & VATTR_DEFINED) == 0) || + (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) && + (vertexType & TEXCOORD_DEFINED) == 0)) { + + return; + } + + // Either non-interleaved, by-ref or nio buffer + if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) { + // Java array case + // 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 & VATTR_DEFINED) != 0) + vdefined |= VATTR_FLOAT; + if((vertexType & TEXCOORD_DEFINED) != 0) + vdefined |= TEXCOORD_FLOAT; + + Pipeline.getPipeline().buildGAForByRef(cv.ctx, + this, geoType, isNonUniformScale, + updateAlpha, alpha, + ignoreVertexColors, + validVertexCount, + vertexFormat, + vdefined, + initialCoordIndex, + mirrorFloatRefCoords, mirrorDoubleRefCoords, + initialColorIndex, mirrorFloatRefColors[0], mirrorUnsignedByteRefColors[0], + initialNormalIndex, mirrorFloatRefNormals, + vertexAttrCount, vertexAttrSizes, + initialVertexAttrIndex, mirrorFloatRefVertexAttrs, + ((texCoordSetMap == null) ? 0:texCoordSetMap.length), + texCoordSetMap, + initialTexCoordIndex,texCoordStride, + mirrorRefTexCoords, + (xform == null) ? null : xform.mat, + (nxform == null) ? null : nxform.mat); + } + /* + // NOTE: NIO buffers are no longer supported in display lists. + // This was never enabled by default anyway (only when the + // optimizeForSpace property was set to false), so it wasn't + // well-tested. If future support is desired, we will need to + // add vertex attributes to buildGAForBuffer. There are no plans + // to ever do this. + else { + // NIO Buffer case + 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; + // NOTE : need to add vertex attrs + Pipeline.getPipeline().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); + } + } + + private void unIndexifyJavaArray(IndexedGeometryArrayRetained src) { +// System.err.println("unIndexifyJavaArray"); + + 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.err.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.err.println("===> copying color3"); + System.arraycopy(vdata, + src.indexColor[index]*src.stride + src.colorOffset, + vertexData, vOffset + colorOffset, colorStride); + } else if (colorStride == 3) { + // System.err.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, + (src.indexTexCoord[i][index])*src.stride + src.textureOffset + interleavedOffset, + vertexData, tcOffset, texCoordStride); + } + } + + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + // vertex attributes can't be interleaved + assert (src.vertexFormat & GeometryArray.INTERLEAVED) == 0; + + for (i = 0; i < vertexAttrCount; i++) { + int vaOffset = vOffset + vertexAttrOffsets[i]; + + System.arraycopy(vdata, + (src.indexVertexAttr[i][index])*src.stride + src.vertexAttrOffsets[i], + vertexData, vaOffset, vertexAttrSizes[i]); + } + } + + if ((vertexFormat & GeometryArray.COORDINATES) != 0){ + // System.err.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], + 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 = 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 = 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.VERTEX_ATTRIBUTES) != 0) { + vOffset = 0; + switch (src.vertexType & VATTR_DEFINED) { + case AF: + for (index=start; index < end; index++) { + for (i = 0; i < vertexAttrCount; i++) { + int vaOffset = vOffset + vertexAttrOffsets[i]; + System.arraycopy(src.floatRefVertexAttrs[i], + src.indexVertexAttr[i][index]*vertexAttrSizes[i], + vertexData, vaOffset, vertexAttrSizes[i]); + } + vOffset += stride; + } + 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; + } + } + + } + } + + + private void unIndexifyNIOBuffer(IndexedGeometryArrayRetained src) { +// System.err.println("unIndexifyNIOBuffer"); + + int vOffset = 0, srcOffset, tOffset = 0; + int index, colorStride = 0; + 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.err.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((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; + if ((src.vertexType & TEXCOORD_DEFINED) != 0) { + for (index=start; index < end; index++) { + for (i = 0, tOffset = vOffset; + i < texCoordSetCount; i++) { + FloatBuffer texBuffer = (FloatBuffer)src.refTexCoordsBuffer[i].getROBuffer(); + texBuffer.position(src.indexTexCoord[i][index]*texCoordStride); + texBuffer.get(vertexData, tOffset, texCoordStride); + tOffset += texCoordStride; + } + vOffset += stride; + } + } + } + + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + vOffset = 0; + if ((src.vertexType & VATTR_DEFINED) == AF) { + for (index=start; index < end; index++) { + for (i = 0; i < vertexAttrCount; i++) { + int vaOffset = vOffset + vertexAttrOffsets[i]; + FloatBuffer vaBuffer = src.floatBufferRefVertexAttrs[i]; + vaBuffer.position(src.indexVertexAttr[i][index]*vertexAttrSizes[i]); + vaBuffer.get(vertexData, vaOffset, vertexAttrSizes[i]); + } + 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; + } + + if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + stride += vertexAttrStride; + } + + //System.err.println("stride() = " + stride); + 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 stride of the set of vertex attributes. This is the + * sum of the sizes of each vertex attribute. + * @return the stride of the vertex attribute data + */ + int vertexAttrStride() { + int sum = 0; + for (int i = 0; i < vertexAttrCount; i++) { + sum += vertexAttrSizes[i]; + } + return sum; + } + + /** + * Returns the offset in number of floats from the start of a vertex to + * each per-vertex vertex attribute. + * @return array of offsets in floats vertex start to the vertex attribute data + */ + int[] vertexAttrOffsets() { + int[] offsets; + + // Create array of offsets to the start of each vertex attribute. + // The offset of the first attribute is always 0. If no vertex attributes exist, + // then we will allocate an array of length 1 to avoid some checking elsewhere. + if (vertexAttrCount > 0) { + offsets = new int[vertexAttrCount]; + } + else { + offsets = new int[1]; + } + offsets[0] = 0; + for (int i = 1; i < vertexAttrCount; i++) { + offsets[i] = offsets[i-1] + vertexAttrSizes[i-1]; + } + + return offsets; + } + + /** + * Returns the offset in number of floats from the start of a vertex to + * the per-vertex texture coordinate data. + * texture coordinate data always follows vertex attribute data + * @return the offset in floats vertex start to the tetxure data + */ + int textureOffset() + { + int offset = vertexAttrOffsets[0]; + + if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + offset += vertexAttrStride; + } + + return offset; + } + + /** + * 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 + */ + @Override + int getVertexFormat(){ + return vertexFormat; + } + + /** + * Retrieves the number of vertex attributes in this GeometryArray + * object. + * + * @return the number of vertex attributes in this GeometryArray + * object + */ + int getVertexAttrCount() { + return vertexAttrCount; + } + + + /** + * Retrieves the vertex attribute sizes array from this + * GeometryArray object. + * + * @param vertexAttrSizes an array that will receive a copy of + * the vertex attribute sizes array. The array must hold at least + * vertexAttrCount elements. + */ + void getVertexAttrSizes(int[] vertexAttrSizes) { + for (int i = 0; i < vertexAttrCount; i++) { + vertexAttrSizes[i] = this.vertexAttrSizes[i]; + } + } + + + + void sendDataChangedMessage(boolean coordinatesChanged) { + J3dMessage[] m; + int i, j, k, numShapeMessages, numMorphMessages; + + synchronized(liveStateLock) { + if (source != null && source.isLive()) { + // System.err.println("In GeometryArrayRetained - "); + + // Send a message to renderBin to rebuild the display list or + // process the vertex array accordingly + // XXXX: 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(); + + ArrayList shapeList = userLists.get(i); + for (j = 0; j < shapeList.size(); j++) { + Shape3DRetained s = 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++) { + Shape3DRetained s = 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] = new J3dMessage(); + + 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 = 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++) { + ArrayList morphList = morphUserLists.get(i); + for (j = 0; j < morphList.size(); j++) { + morphList.get(j).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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= COORDINATE_CHANGED; + + this.vertexData[offset] = coordinate[0]; + this.vertexData[offset+1]= coordinate[1]; + this.vertexData[offset+2]= coordinate[2]; + + if (isLive) { + geomLock.unLock(); + } + if (inUpdater || (source == null)) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + + if(isLive) { + geomLock.unLock(); + } + + if (inUpdater || (source == null)) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= COORDINATE_CHANGED; + this.vertexData[offset] = coordinate.x; + this.vertexData[offset+1]= coordinate.y; + this.vertexData[offset+2]= coordinate.z; + + if(isLive) { + geomLock.unLock(); + } + if (inUpdater || (source == null)) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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; + if(isLive) { + geomLock.unLock(); + } + if (inUpdater || source == null ) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + + if(isLive) { + geomLock.unLock(); + } + if (inUpdater ||source == null ) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + + if(isLive) { + geomLock.unLock(); + } + + if (inUpdater ||source == null ) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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; + } + if(isLive) { + geomLock.unLock(); + } + + if (inUpdater ||source == null ) { + return; + } + if (!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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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; + } + if(isLive) { + geomLock.unLock(); + } + + if (inUpdater ||source == null ) { + return; + } + if (!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 start for length vertices. + * @param index the vertex index + * @param coordinates an array of vectors containing new coordinates + * @param start starting vertex index of data in coordinates . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + if(isLive) { + geomLock.unLock(); + } + if (inUpdater ||source == null ) { + return; + } + if (!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 start for length 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 coordinates . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + + if(isLive) { + geomLock.unLock(); + } + if (inUpdater || (source == null)) { + return; + } + if (!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 start for length vertices. + * @param index the vertex index + * @param coordinates an array of vectors containing new coordinates + * @param start starting vertex index of data in coordinates . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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; + } + + if(isLive) { + geomLock.unLock(); + } + + if (inUpdater || (source == null)) { + return; + } + if (!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 start for length vertices. + * @param index the vertex index + * @param coordinates an array of vectors containing new coordinates + * @param start starting vertex index of data in coordinates . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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; + } + + if(isLive) { + geomLock.unLock(); + } + if (inUpdater || (source == null)) { + return; + } + if (!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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive) { + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive){ + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the colors associated with the vertices starting at + * the specified index for this object using data in colors + * starting at index start for length 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 colors. + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the colors associated with the vertices starting at + * the specified index for this object using data in colors + * starting at index start for length 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 colors. + * @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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the colors associated with the vertices starting at + * the specified index for this object using data in colors + * starting at index start for length 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 colors. + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the colors associated with the vertices starting at + * the specified index for this object using data in colors + * starting at index start for length 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 colors. + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the colors associated with the vertices starting at + * the specified index for this object using data in colors + * starting at index start for length 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 colors. + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the colors associated with the vertices starting at + * the specified index for this object using data in colors + * starting at index start for length 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 colors. + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= NORMAL_CHANGED; + this.vertexData[offset] = normal[0]; + this.vertexData[offset+1] = normal[1]; + this.vertexData[offset+2] = normal[2]; + + if(isLive) { + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= NORMAL_CHANGED; + this.vertexData[offset] = normal.x; + this.vertexData[offset+1] = normal.y; + this.vertexData[offset+2] = normal.z; + + if(isLive){ + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + 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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the normals associated with the vertices starting at + * the specified index for this object using data in normals + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param normals the new normals + * @param start starting normal index of data in colors . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the normals associated with the vertices starting at + * the specified index for this object using data in normals + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param normals the new normals + * @param start starting normal index of data in colors . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + + /** + * Sets the texture coordinates associated with the vertices starting at + * the specified index for this object using data in texCoords + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param texCoords the new texture coordinates + * @param start starting texture coordinate index of data in texCoords . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the texture coordinates associated with the vertices starting at + * the specified index for this object using data in texCoords + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param texCoords the new texture coordinates + * @param start starting texture coordinate index of data in texCoords . + * @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; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the texture coordinates associated with the vertices starting at + * the specified index for this object using data in texCoords + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param texCoords the new texture coordinates + * @param start starting texture coordinate index of data in texCoords . + * @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; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the texture coordinates associated with the vertices starting at + * the specified index for this object using data in texCoords + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param texCoords the new texture coordinates + * @param start starting texture coordinate index of data in texCoords . + * @param length number of texture Coordinates to be copied. + */ + void setTextureCoordinates(int texCoordSet, int index, TexCoord2f texCoords[], + int start, int length) { + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + + } + + /** + * Sets the texture coordinates associated with the vertices starting at + * the specified index for this object using data in texCoords + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param texCoords the new texture coordinates + * @param start starting texture coordinate index of data in texCoords . + * @param length number of texture Coordinates to be copied. + */ + void setTextureCoordinates(int texCoordSet, int index, + TexCoord3f texCoords[], + int start, int length) { + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the texture coordinates associated with the vertices starting at + * the specified index for this object using data in texCoords + * starting at index start and ending at index start+length. + * @param index the vertex index + * @param texCoords the new texture coordinates + * @param start starting texture coordinate index of data in texCoords . + * @param length number of texture Coordinates to be copied. + */ + void setTextureCoordinates(int texCoordSet, int index, + TexCoord4f texCoords[], + int start, int length) { + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr the Point2f containing the new vertex attribute + */ + void setVertexAttr(int vertexAttrNum, int index, + Point2f vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + this.vertexData[offset] = vertexAttr.x; + this.vertexData[offset+1] = vertexAttr.y; + + if(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr the Point3f containing the new vertex attribute + */ + void setVertexAttr(int vertexAttrNum, int index, + Point3f vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + this.vertexData[offset] = vertexAttr.x; + this.vertexData[offset+1] = vertexAttr.y; + this.vertexData[offset+2] = vertexAttr.z; + + if (isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attribute associated with the vertex at the + * specified index in the specified vertex attribute number for + * this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index destination vertex index in this geometry array + * @param vertexAttr the Point4f containing the new vertex attribute + */ + void setVertexAttr(int vertexAttrNum, int index, + Point4f vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + this.vertexData[offset] = vertexAttr.x; + this.vertexData[offset+1] = vertexAttr.y; + this.vertexData[offset+2] = vertexAttr.z; + this.vertexData[offset+3] = vertexAttr.w; + + if(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of 1*n, 2*n, 3*n, or 4*n values + * containing n new vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes to be copied. + */ + void setVertexAttrs(int vertexAttrNum, int index, + float[] vertexAttrs, + int start, int length) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int size = vertexAttrSizes[vertexAttrNum]; + int i, j, k; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + for (i = start * size, j = offset, k = 0; k < length; i += size, j += this.stride, k++) { + for (int ii = 0; ii < size; ii++) { + this.vertexData[j+ii] = vertexAttrs[i+ii]; + } + } + + if(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point2f objects containing new + * vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes to be copied. + */ + void setVertexAttrs(int vertexAttrNum, int index, + Point2f[] vertexAttrs, + int start, int length) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int i, j, k; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + for (i = start, j = offset, k = 0; k < length; i++, j += this.stride, k++) { + this.vertexData[j] = vertexAttrs[i].x; + this.vertexData[j+1] = vertexAttrs[i].y; + } + if(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point3f objects containing new + * vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes to be copied. + */ + void setVertexAttrs(int vertexAttrNum, int index, + Point3f[] vertexAttrs, + int start, int length) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int i, j, k; + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + for (i = start, j = offset, k = 0; k < length; i++, j += this.stride, k++) { + this.vertexData[j] = vertexAttrs[i].x; + this.vertexData[j+1] = vertexAttrs[i].y; + this.vertexData[j+2] = vertexAttrs[i].z; + } + if(isLive) { + geomLock.unLock(); + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attributes associated with the vertices + * starting at the specified index in the specified vertex + * attribute number for this object using data in + * vertexAttrs starting at index start and + * ending at index start+length. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index starting destination vertex index in this geometry array + * @param vertexAttrs source array of Point4f objects containing new + * vertex attributes + * @param start starting source vertex index in vertexAttrs + * array. + * @param length number of vertex attributes to be copied. + */ + void setVertexAttrs(int vertexAttrNum, int index, + Point4f[] vertexAttrs, + int start, int length) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int i, j, k; + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + + for (i = start, j = offset, k = 0; k < length; i++, j += this.stride, k++) { + this.vertexData[j] = vertexAttrs[i].x; + this.vertexData[j+1] = vertexAttrs[i].y; + this.vertexData[j+2] = vertexAttrs[i].z; + this.vertexData[j+3] = vertexAttrs[i].w; + } + if(isLive) { + 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]; + } + } + + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttr(int vertexAttrNum, int index, + float[] vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int size = vertexAttrSizes[vertexAttrNum]; + + for (int i = 0; i < size; i++) { + vertexAttr[i] = this.vertexData[offset+i]; + + } + + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttr(int vertexAttrNum, int index, + Point2f vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + + vertexAttr.x = this.vertexData[offset]; + vertexAttr.y = this.vertexData[offset+1]; + + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttr(int vertexAttrNum, int index, + Point3f vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + + vertexAttr.x = this.vertexData[offset]; + vertexAttr.y = this.vertexData[offset+1]; + vertexAttr.z = this.vertexData[offset+2]; + + } + + /** + * Gets the vertex attribute associated with the vertex at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttr(int vertexAttrNum, int index, + Point4f vertexAttr) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + + vertexAttr.x = this.vertexData[offset]; + vertexAttr.y = this.vertexData[offset+1]; + vertexAttr.z = this.vertexData[offset+2]; + vertexAttr.w = this.vertexData[offset+3]; + + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttrs(int vertexAttrNum, int index, + float[] vertexAttrs) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int size = vertexAttrSizes[vertexAttrNum]; + int i, j, k; + + for (i = 0, j = offset; + ((i < vertexAttrs.length) && (j < this.vertexData.length)) ; + i += size, j += this.stride) { + for (k = 0; k < size; k++) { + vertexAttrs[i+k] = this.vertexData[j+k]; + } + } + + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttrs(int vertexAttrNum, int index, + Point2f[] vertexAttrs) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int i, j; + + for (i = 0, j = offset; + ((i < vertexAttrs.length) && (j < this.vertexData.length)) ; + i++, j += this.stride) { + vertexAttrs[i].x = this.vertexData[j]; + vertexAttrs[i].y = this.vertexData[j+1]; + } + + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttrs(int vertexAttrNum, int index, + Point3f[] vertexAttrs) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int i, j; + + for (i = 0, j = offset; + ((i < vertexAttrs.length) && (j < this.vertexData.length)) ; + i++, j += this.stride) { + vertexAttrs[i].x = this.vertexData[j]; + vertexAttrs[i].y = this.vertexData[j+1]; + vertexAttrs[i].z = this.vertexData[j+2]; + } + + } + + /** + * Gets the vertex attributes associated with the vertices starting at + * the specified index in the specified vertex attribute number + * for this object. + */ + public void getVertexAttrs(int vertexAttrNum, int index, + Point4f[] vertexAttrs) { + + int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum]; + int i, j; + + for (i = 0, j = offset; + ((i < vertexAttrs.length) && (j < this.vertexData.length)) ; + i++, j += this.stride) { + vertexAttrs[i].x = this.vertexData[j]; + vertexAttrs[i].y = this.vertexData[j+1]; + vertexAttrs[i].z = this.vertexData[j+2]; + vertexAttrs[i].w = this.vertexData[j+3]; + } + + } + + + /** + * 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) { + // XXXX: 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(texCoordType); + setupMirrorVertexAttrPointer(vertexAttrType); + nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0); + } + } + } + + //NVaidya + // User may or may not have changed indices in updater callback. + // We need to presume that the user may indeed have and, thus, will + // need to recompute maxCoordIndex unconditionally while + // geomLock is still locked. + if ((vertexFormat & GeometryArray.BY_REFERENCE_INDICES) != 0) { + assert (this instanceof IndexedGeometryArrayRetained); + + if (((IndexedGeometryArrayRetained)this).getCoordIndicesRef() == null) { + nullGeo = true; + } + ((IndexedGeometryArrayRetained)this).doPostUpdaterUpdate(); + } + } + + 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; + int out[] = new int[6]; + + //Do trivial vertex test. + for(i=0; i<6; i++) + out[i] = 0; + for(i=0; i= 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] = new Point3d(); + + Point3d boxCenter = new Point3d(); + box.getCenter(boxCenter); + + // 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, boxCenter, null, dist, iPnt); + } + 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, boxCenter, null, dist, iPnt); + } + 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, boxCenter, null, dist, iPnt); + } + 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, boxCenter, null, dist, iPnt); + } + 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, boxCenter, null, dist, iPnt); + } + 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, boxCenter, null, dist, iPnt); + } + return true; + } + return false; + } + + + boolean intersectBoundingSphere(Point3d coordinates[], + BoundingSphere sphere, + double dist[], + Point3d iPnt) + { + int i, j; + Vector3d tempV3D = new Vector3d(); + boolean esFlag; + + //Do trivial vertex test. + + for (i=0; i 0.0) + break; + } + + for(j=i; j 0.0) + break; + } + + if(j == (coordinates.length-1)) { + // System.err.println("(1) Degenerate polygon."); + return false; // Degenerate polygon. + } + + /* + for(i=0; i sphere.radius) { + 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); + } + return true; + } + 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) { + polytope.getCenter(iPnt); + + double x = tP4d.x - iPnt.x; + double y = tP4d.y - iPnt.y; + double z = tP4d.z - iPnt.z; + dist[0] = Math.sqrt(x * x + y * y + z * z); + iPnt.x = tP4d.x; + iPnt.y = tP4d.y; + iPnt.z = tP4d.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.err.println("The value of the input vertices are: "); + for(int i=0; i < coordinates.length; i++) { + System.err.println("The " +i+ " th vertex is: " + coordinates[i]); + } + + System.err.println("The value of the input bounding Polytope's planes ="); + for(int i=0; i < polytope.planes.length; i++) { + System.err.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.err.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.err.print(problemTableau[i][j] + " "); + } + System.err.println(); + } + } + + double distance = generalStandardSimplexSolver(problemTableau, + Float.NEGATIVE_INFINITY); + if(debug) { + System.err.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; + double multiplier; + + if(debug) { + System.err.println("The number of rows is : " + numRow); + System.err.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.err.println("input problem tableau is:"); + for(int k=0; k < numRow; k++) { + for(int j=0; j < numCol; j++) { + System.err.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.err.println("Found an optimal solution"); + optimal = true; + } + + //System.err.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.err.println("Division by zero has occurred"); + System.err.println("Within the linear program solver"); + System.err.println("Ignoring the zero as a potential pivot"); + } + } else if ( (element < 0.0) || (endElement < 0.0) ){ + if(debug) { + System.err.println("Ignoring cases where element is negative"); + System.err.println("The value of element is: " + element); + System.err.println("The value of end Element is: " + endElement); + } + } else { + ratio = endElement/element; // should be s/r + if(debug) { + System.err.println("The value of element is: " + element); + System.err.println("The value of endElement is: " + endElement); + System.err.println("The value of ratio is: " + ratio); + System.err.println("The value of prevRatio is: " + prevRatio); + System.err.println("Value of ratio <= prevRatio is :" + + (ratio <= prevRatio)); + } + if(ratio <= prevRatio) { + if(debug) { + System.err.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.err.println("UNABLE TO FIND SOLUTION"); + System.err.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.err.println("The value of row index is: " + pivotRowIndex); + System.err.println("The value of col index is: " + pivotColIndex); + System.err.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 = new Vector3d(); + Vector3d ap = new Vector3d(); + + 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) { + return false; // line segment points away from sphere. + } + + abLenSq = ab.lengthSquared(); + acLenSq = abDotAp * abDotAp / abLenSq; + + if(acLenSq < abLenSq) { + return false; // C doesn't lies between end points of edge. + } + + radiusSq = sphere.radius * sphere.radius; + apLenSq = ap.lengthSquared(); + + if((apLenSq - acLenSq) <= radiusSq) { + return true; + } + + 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.err.println("Normal " + normal + " axis " + axis ); + + for(i=0; i0.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 = new Vector3d(); + Vector3d direction = new Vector3d(); + double pD, pNrmDotrDir, tr; + + // Compute plane D. + tempV3d.set(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.err.println("Edge is parallel to plane."); + return false; + } + + tempV3d.set(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.err.println("Edge intersects the plane behind the start or exceed end."); + return false; + } + + iPnt.x = start.x + tr * direction.x; + iPnt.y = start.y + tr * direction.y; + iPnt.z = start.z + tr * direction.z; + + return true; + + } + + // Assume coord is CCW. + 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.err.println("Normal " + normal + " axis " + axis ); + + for(i=0; i 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 = new Vector3d(); + Vector3d e2 = new Vector3d(); + Vector3d n1 = new Vector3d(); + Vector3d tempV3d = new Vector3d(); + + 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.err.println("(1) Degenerate triangle."); + return false; // Degenerate triangle. + } + + tempV3d.set(v0); + d1 = - n1.dot(tempV3d); // plane equation 1: n1.x + d1 = 0 + + // put u to compute signed distance to the plane. + tempV3d.set(u); + du = n1.dot(tempV3d) + d1; + + // coplanarity robustness check + if(Math.abs(du) 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)) { + return true; + } + + 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.err.println("In intersectTriTri ..."); + Vector3d e1 = new Vector3d(); + Vector3d e2 = new Vector3d(); + Vector3d n1 = new Vector3d(); + Vector3d n2 = new Vector3d(); + Vector3d tempV3d = new Vector3d(); + + 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.err.println("(1) Degenerate triangle."); + return false; // Degenerate triangle. + } + + tempV3d.set(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(u0); + du0 = n1.dot(tempV3d) + d1; + tempV3d.set(u1); + du1 = n1.dot(tempV3d) + d1; + tempV3d.set(u2); + du2 = n1.dot(tempV3d) + d1; + + // coplanarity robustness check + if(Math.abs(du0)0.0 && du0du2>0.0) { + // System.err.println("In intersectTriTri : du0du1>0.0 && du0du2>0.0"); + 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.err.println("(2) Degenerate triangle."); + return false; // Degenerate triangle. + } + + tempV3d.set(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(v0); + dv0 = n2.dot(tempV3d) + d2; + tempV3d.set(v1); + dv1 = n2.dot(tempV3d) + d2; + tempV3d.set(v2); + dv2 = n2.dot(tempV3d) + d2; + + // coplanarity robustness check + if(Math.abs(dv0)0.0 && dv0dv2>0.0) { + // System.err.println("In intersectTriTri : dv0dv1>0.0 && dv0dv2>0.0"); + 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); + 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.err.println("In intersectTriTri : coplanarTriTri test 2"); + boolean toreturn = coplanarTriTri(n2, v0, v1, v2, u0, u1, u2); + 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 0.0) + break; + } + + for(j=i; j 0.0) + break; + } + + if(j == (coord1.length-1)) { + // System.err.println("(1) Degenerate polygon."); + return false; // Degenerate polygon. + } + + /* + for(i=0; i1) { + break; + } + } + } + + if (j==0) { + return false; + } + + if (coord2.length < 3) { + boolean toreturn = pointIntersectPolygon2D(pNrm, coord1, seg[0]); + return toreturn; + } else { + boolean toreturn = edgeIntersectPolygon2D(pNrm, coord1, seg); + 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 = new Vector3d(); + 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); + 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 = new Vector3d(); + vec1 = new Vector3d(); + pNrm = new Vector3d(); + + 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 0.0) { + break; + } + } + + + for (j=l; j 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 + 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; + } + } + return isIntersect; + } + + // Plane equation: (p - p0)*pNrm = 0 or p*pNrm = pD; + tempV3d = new Vector3d(); + tempV3d.set(coordinates[0]); + pD = pNrm.dot(tempV3d); + tempV3d.set(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 + 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. + } + } + } + } + } else { + for (i=0; i -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 { + //degenerate line=>point + } + } + } + } + } + } else { + if (absNrmY < absNrmZ) { + for (i=0; i -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 { + //degenerate line=>point + } + } + } + } + } else { + for (i=0; i -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 { + //degenerate line=>point + } + } + } + } + } + } + + if (isIntersect) { + dist[0] *= direction.length(); + } + 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 pD = 0.0; + Vector3d tempV3d = new Vector3d(); + + int i, j; + + // Compute plane normal. + for(i=0; i 0.0) + break; + } + + for(j=i; j 0.0) + break; + } + + if(j == (coordinates.length-1)) { + // System.err.println("(1) Degenerate polygon."); + return false; // Degenerate polygon. + } + + /* + System.err.println("Ray orgin : " + ray.origin + " dir " + ray.direction); + System.err.println("Triangle/Quad :"); + for(i=0; i (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.err.println("GeometryArrayRetained : intersectLineAndRay"); + // System.err.println("start " + start + " end " + end ); + // System.err.println("ori " + ori + " dir " + dir); + + lDir = new Vector3d(); + 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); + } + } + 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.err.println("Before the origin of ray " + s); + return false; + } + if((t<0)||(t>1.0)) {// Before or after the end points of line. + // System.err.println("Before or after the end points of line. " + t); + return false; + } + + tmp1 = ori.z + s * dir.z; + tmp2 = start.z + t * lDir.z; + + if((tmp1 < (tmp2 - EPS)) || (tmp1 > (tmp2 + EPS))) { + // System.err.println("No intersection : tmp1 " + tmp1 + " tmp2 " + tmp2); + 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.err.println("Intersected : tmp1 " + tmp1 + " tmp2 " + tmp2); + 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 = new Point3d(); + Point3d end = new Point3d(); + Vector3d direction = new Vector3d(); + Point3d iPnt1 = new Point3d(); + Vector3d originToIpnt = new Vector3d(); + + 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)) { + return true; + } + } + else { + if (intersectSegment(coordinates, origin, end, dist, iPnt)) { + return true; + } + } + } + + // Ray doesn't intersect, check distance to edges + double sqDistToEdge; + int j; + for (int i=0; i 2) { + if (cone instanceof PickConeRay) { + if (intersectRay(coordinates, + new PickRay (origin, direction), + dist, iPnt)) { + return true; + } + } + else { + if (intersectSegment(coordinates, origin, end, dist, iPnt)) { + return true; + } + } + } + + // Ray doesn't intersect, check distance to edges + double sqDistToEdge; + int j = 0; + for (int i=0; i= coords.getROBuffer().limit()) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23")); + } + } else if (coords.getROBuffer().limit() < (3*(initialCoordIndex+validVertexCount))) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99")); + } + } + + // lock the geometry and start to do real work + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= COORDINATE_CHANGED; + coordRefBuffer = coords; + if(coords == null) { + floatBufferRefCoords = null; + doubleBufferRefCoords = null; + // XXXX: if not mix java array with nio buffer + // vertexType can be used as vertexTypeBuffer + vertexType &= ~PD; + vertexType &= ~PF; + }else { + switch (coords.bufferType) { + case FLOAT: + floatBufferRefCoords = (FloatBuffer)coords.getROBuffer(); + doubleBufferRefCoords = null; + vertexType |= PF; + vertexType &= ~PD; + break; + case DOUBLE: + floatBufferRefCoords = null; + doubleBufferRefCoords = (DoubleBuffer)coords.getROBuffer(); + vertexType |= PD; + vertexType &= ~PF; + break; + default: + break; + } + } + + // need not call setupMirrorVertexPointer() since + // we are not going to set mirror in NIO buffer case + // XXXX: if we need to mix java array with buffer, + // we may need to consider setupMirrorVertexPointer() + + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && source != null) { + if (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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && source != null) { + if (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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && source != null) { + if (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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && source != null) { + if (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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && source != null) { + if (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")); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + + } + + float[] getColorRefFloat() { + return floatRefColors; + } + + + // set the color with nio buffer + void setColorRefBuffer(J3DBuffer colors) { + if (colors != null) { + switch(colors.bufferType) { + case FLOAT: + assert ((FloatBuffer)colors.getROBuffer()).isDirect(); + break; + case BYTE: + assert ((ByteBuffer)colors.getROBuffer()).isDirect(); + break; + case 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.getROBuffer().limit()) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24")); + } + } else if (colors.getROBuffer().limit() < + getColorStride() * (initialColorIndex+validVertexCount)) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112")); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= COLOR_CHANGED; + colorChanged = 0xffff; + colorRefBuffer = colors; + if(colors == null) { + floatBufferRefColors = null; + byteBufferRefColors = null; + } else { + switch (colors.bufferType) { + case FLOAT: + floatBufferRefColors = (FloatBuffer)colors.getROBuffer(); + byteBufferRefColors = null; + break; + + case BYTE: + byteBufferRefColors = (ByteBuffer)colors.getROBuffer(); + 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.bufferType) { + case FLOAT: + vertexType |= CF; + vertexType &= ~CUB; + break; + + case BYTE: + vertexType |= CUB; + vertexType &= ~CF; + break; + default: + break; + } + } + } + else { + setupMirrorColorPointer(CF|CUB, false); + } + + if(isLive) { + geomLock.unLock(); + } + + if (!inUpdater && 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive){ + geomLock.unLock(); + } + + if (!inUpdater && 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")); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + + if(isLive) { + geomLock.unLock(); + } + + if (!inUpdater && 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + + if (!inUpdater && 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")); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + + if(isLive) { + geomLock.unLock(); + } + + if (!inUpdater && 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + + } + + float[] getNormalRefFloat() { + return floatRefNormals; + } + + // setup the normal with nio buffer + void setNormalRefBuffer(J3DBuffer normals) { + + FloatBuffer bufferImpl = null; + + if (normals != null) { + if(normals.bufferType != J3DBuffer.Type.FLOAT) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116")); + + bufferImpl = (FloatBuffer)normals.getROBuffer(); + + assert bufferImpl.isDirect(); + + if ((vertexFormat & GeometryArray.NORMALS) == 0) { + throw new IllegalStateException(J3dI18N.getString("GeometryArray122")); + } + + if (this instanceof IndexedGeometryArrayRetained) { + IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this; + if (idx.maxNormalIndex * 3 >= + ((FloatBuffer)normals.getROBuffer()).limit()) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26")); + } + } else if (bufferImpl.limit() < 3 * (initialNormalIndex + validVertexCount )) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= NORMAL_CHANGED; + normalRefBuffer = normals; + + if (normals == null) { + vertexType &= ~NF; + floatBufferRefNormals = null; + } + else { + vertexType |= NF; + floatBufferRefNormals = bufferImpl; + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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 (texCoordType != 0 && texCoordType != TF) { + if (texCoords != null) { + throw new IllegalArgumentException( + J3dI18N.getString("GeometryArray98")); + } + return; + } + + if (texCoords != null) { + + 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= TEXTURE_CHANGED; + refTexCoords[texCoordSet] = texCoords; + if (inUpdater || (this instanceof IndexedGeometryArrayRetained && + ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) { + texCoordType = TF; + validateTexCoordPointerType(); + } + else { + setupMirrorTexCoordPointer(texCoordSet, TF); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + + float[] getTexCoordRefFloat(int texCoordSet) { + return ((float[])refTexCoords[texCoordSet]); + } + + // set the tex coord with nio buffer + void setTexCoordRefBuffer(int texCoordSet, J3DBuffer texCoords) { + + FloatBuffer bufferImpl = null; + + if (texCoords != null) { + if(texCoords.bufferType != J3DBuffer.Type.FLOAT) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116")); + + bufferImpl = (FloatBuffer)texCoords.getROBuffer(); + int bufferSize = bufferImpl.limit(); + + assert bufferImpl.isDirect(); + + 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")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= TEXTURE_CHANGED; + // refTexCoordsBuffer contains J3DBuffer object for tex coord + refTexCoordsBuffer[texCoordSet] = texCoords; + if (texCoords == null) { + refTexCoords[texCoordSet] = null; + } + else { + // refTexCoords contains NIOBuffer object for tex coord + refTexCoords[texCoordSet] = bufferImpl; + } + texCoordType = TF; + validateTexCoordPointerType(); + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + J3DBuffer getTexCoordRefBuffer(int texCoordSet) { + return refTexCoordsBuffer[texCoordSet]; + } + + void setTexCoordRef2f(int texCoordSet, TexCoord2f[] texCoords) { + + if (texCoordType != 0 && texCoordType != T2F) { + if (texCoords != null) { + throw new IllegalArgumentException( + J3dI18N.getString("GeometryArray98")); + } + return; + } + + if (texCoords != null) { + 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")); + } + + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= TEXTURE_CHANGED; + refTexCoords[texCoordSet] = texCoords; + if (inUpdater || (this instanceof IndexedGeometryArrayRetained && + ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) { + texCoordType = T2F; + validateTexCoordPointerType(); + } + else { + setupMirrorTexCoordPointer(texCoordSet, T2F); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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 (texCoordType != 0 && texCoordType != T3F) { + if (texCoords != null) { + throw new IllegalArgumentException( + J3dI18N.getString("GeometryArray98")); + } + return; + } + + if (texCoords != null) { + + 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")); + } + + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= TEXTURE_CHANGED; + refTexCoords[texCoordSet] = texCoords; + if (inUpdater || (this instanceof IndexedGeometryArrayRetained && + ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) { + texCoordType = T3F; + validateTexCoordPointerType(); + } + else { + setupMirrorTexCoordPointer(texCoordSet, T3F); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + + TexCoord3f[] getTexCoordRef3f(int texCoordSet) { + if (refTexCoords != null && refTexCoords[texCoordSet] != null && + refTexCoords[texCoordSet] instanceof TexCoord3f[]) { + return ((TexCoord3f[])refTexCoords[texCoordSet]); + } else { + return null; + } + } + + + /** + * Sets the float vertex attribute array reference for the + * specified vertex attribute number to the specified array. + */ + void setVertexAttrRefFloat(int vertexAttrNum, float[] vertexAttrs) { + + // XXXX: Add the following test if we ever add double-precision types + /* + if (vertexAttrType != 0 && vertexAttrType != AF) { + if (vertexAttrs != null) { + // XXXX: new exception string + throw new IllegalArgumentException( + J3dI18N.getString("GeometryArray98-XXX")); + } + return; + } + */ + + if (vertexAttrs != null) { + int sz = vertexAttrSizes[vertexAttrNum]; + + if (this instanceof IndexedGeometryArrayRetained) { + IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this; + + if (sz*idx.maxVertexAttrIndices[vertexAttrNum] >= vertexAttrs.length) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30")); + } + + } else if (vertexAttrs.length < sz*(initialVertexAttrIndex[vertexAttrNum] + validVertexCount) ) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray129")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + floatRefVertexAttrs[vertexAttrNum] = vertexAttrs; + if (inUpdater || (this instanceof IndexedGeometryArrayRetained && + ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) { + vertexAttrType = AF; + validateVertexAttrPointerType(); + } + else { + setupMirrorVertexAttrPointer(vertexAttrNum, AF); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * Gets the float vertex attribute array reference for the specified + * vertex attribute number. + */ + float[] getVertexAttrRefFloat(int vertexAttrNum) { + return floatRefVertexAttrs[vertexAttrNum]; + } + + + /** + * Sets the vertex attribute buffer reference for the specified + * vertex attribute number to the specified buffer object. + */ + void setVertexAttrRefBuffer(int vertexAttrNum, J3DBuffer vertexAttrs) { + + FloatBuffer bufferImpl = null; + + if (vertexAttrs != null) { + if(vertexAttrs.bufferType != J3DBuffer.Type.FLOAT) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116")); + + bufferImpl = (FloatBuffer)vertexAttrs.getROBuffer(); + int bufferSize = bufferImpl.limit(); + + assert bufferImpl.isDirect(); + + int sz = vertexAttrSizes[vertexAttrNum]; + + if (this instanceof IndexedGeometryArrayRetained) { + IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this; + + if (idx.maxVertexAttrIndices[vertexAttrNum] * sz >= bufferSize) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30")); + } + } else if (bufferSize < sz*(initialVertexAttrIndex[vertexAttrNum] + validVertexCount)) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray129")); + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + vertexAttrsRefBuffer[vertexAttrNum] = vertexAttrs; + if (vertexAttrs == null) { + floatBufferRefVertexAttrs[vertexAttrNum] = null; + } + else { + floatBufferRefVertexAttrs[vertexAttrNum] = bufferImpl; + } + vertexAttrType = AF; + validateVertexAttrPointerType(); + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + + } + + /** + * Gets the vertex attribute array buffer reference for the specified + * vertex attribute number. + */ + J3DBuffer getVertexAttrRefBuffer(int vertexAttrNum) { + return vertexAttrsRefBuffer[vertexAttrNum]; + } + + + 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 + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VERTEX_CHANGED; + colorChanged = 0xffff; + interLeavedVertexData = vertexData; + if (inUpdater || (this instanceof IndexedGeometryArrayRetained && + ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) { + setupMirrorInterleavedColorPointer(false); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + processCoordsChanged(vertexData == null); + sendDataChangedMessage(true); + } + } + + // set the interleaved vertex with NIO buffer + void setInterleavedVertexBuffer(J3DBuffer vertexData) { + + FloatBuffer bufferImpl = null; + + if (vertexData != null ){ + + if (vertexData.bufferType != J3DBuffer.Type.FLOAT) + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116")); + + bufferImpl = (FloatBuffer)vertexData.getROBuffer(); + + assert bufferImpl.isDirect(); + + 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 + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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); + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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")); + } + + if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) { + // Interleaved, by-ref + + // 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) { + // Non-interleaved, by-ref + + if ((initialCoordIndex + validVertexCount) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray104")); + } + if ((initialColorIndex + validVertexCount) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray101")); + } + if ((initialNormalIndex + validVertexCount) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray102")); + } + + 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.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + if ((initialVertexAttrIndex[i] + validVertexCount) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString( + "GeometryArray130")); + } + } + } + + 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: + FloatBuffer texBuffer; + for (int i = 0; i < texCoordSetCount; i++) { + texBuffer = (FloatBuffer)refTexCoordsBuffer[i].getROBuffer(); + 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; + } + switch ((vertexType & GeometryArrayRetained.VATTR_DEFINED)) { + case AF: + for (int i = 0; i < vertexAttrCount; i++) { + int sz = vertexAttrSizes[i]; + if (floatBufferRefVertexAttrs[i].limit() < + (sz * (initialVertexAttrIndex[i] + validVertexCount)) ) { + throw new ArrayIndexOutOfBoundsException( + J3dI18N.getString("GeometryArray129")); + } + } + 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")); + } + } + switch ((vertexType & GeometryArrayRetained.VATTR_DEFINED)) { + case AF: + for (int i = 0; i < vertexAttrCount; i++) { + int sz = vertexAttrSizes[i]; + if (floatRefVertexAttrs[i].length < + (sz * (initialVertexAttrIndex[i] + validVertexCount)) ) { + throw new ArrayIndexOutOfBoundsException( + J3dI18N.getString("GeometryArray129")); + } + } + break; + } + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VERTEX_CHANGED; + this.validVertexCount = validVertexCount; + + if(isLive){ + geomLock.unLock(); + } + + if (!inUpdater && 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 + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VERTEX_CHANGED; + this.initialVertexIndex = initialVertexIndex; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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; + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= COORDINATE_CHANGED; + this.initialCoordIndex = initialCoordIndex; + dirtyFlag |= COORDINATE_CHANGED; + if(isLive) { + geomLock.unLock(); + } + // Send a message, since bounds changed + if (!inUpdater && 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; + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= COLOR_CHANGED; + colorChanged = 0xffff; + this.initialColorIndex = initialColorIndex; + if(isLive) { + 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")); + } + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= NORMAL_CHANGED; + this.initialNormalIndex = initialNormalIndex; + if(isLive) { + geomLock.unLock(); + } + // There is no need to send message for by reference, since we + // use VA + } + + int getInitialNormalIndex() { + return initialNormalIndex; + } + + /** + * Sets the initial vertex attribute index for the specified + * vertex attribute number for this GeometryArray object. + */ + void setInitialVertexAttrIndex(int vertexAttrNum, + int initialVertexAttrIndex) { + + if ((initialVertexAttrIndex + validVertexCount) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryArray130")); + } + + int sz = vertexAttrSizes[vertexAttrNum]; + int minLength = sz * (initialVertexAttrIndex + validVertexCount); + if ((vertexType & VATTR_DEFINED) == AF) { + if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) { + if (floatBufferRefVertexAttrs[vertexAttrNum].limit() < minLength) { + throw new ArrayIndexOutOfBoundsException( + J3dI18N.getString("GeometryArray129")); + } + } else { + if (floatRefVertexAttrs[vertexAttrNum].length < minLength ) { + throw new ArrayIndexOutOfBoundsException( + J3dI18N.getString("GeometryArray129")); + } + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= VATTR_CHANGED; + this.initialVertexAttrIndex[vertexAttrNum] = initialVertexAttrIndex; + if(isLive) { + geomLock.unLock(); + } + // There is no need to send message for by reference, since we + // use VA + } + + + /** + * Gets the initial vertex attribute index for the specified + * vertex attribute number for this GeometryArray object. + */ + int getInitialVertexAttrIndex(int vertexAttrNum) { + return initialVertexAttrIndex[vertexAttrNum]; + } + + 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) { + FloatBuffer texBuffer = (FloatBuffer)refTexCoordsBuffer[texCoordSet].getROBuffer(); + 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; + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= TEXTURE_CHANGED; + this.initialTexCoordIndex[texCoordSet] = initialTexCoordIndex; + if(isLive) { + 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]; + } + } + } + + void freeDlistId() { + if (dlistId != -1) { + VirtualUniverse.mc.freeDisplayListId(dlistObj); + dlistId = -1; + } + } + + void assignDlistId() { + if (dlistId == -1) { + dlistObj = VirtualUniverse.mc.getDisplayListId(); + dlistId = dlistObj.intValue(); + } + } + +// Add the specified render atom as a user of this geometry array +// (for the specified render bin) +void addDlistUser(RenderBin renderBin, RenderAtomListInfo ra) { + if (dlistUsers == null) + dlistUsers = new HashMap>(2, 1.0f); + + HashSet raSet = dlistUsers.get(renderBin); + if (raSet == null) { + raSet = new HashSet(); + dlistUsers.put(renderBin, raSet); + } + raSet.add(ra); +} + +// Remove the specified render atom from the set of users of this +// geometry array (for the specified render bin) +void removeDlistUser(RenderBin renderBin, RenderAtomListInfo ra) { + if (dlistUsers == null) + return; + + HashSet raSet = dlistUsers.get(renderBin); + if (raSet == null) + return; + + raSet.remove(ra); +} + +// Returns true if the set of render atoms using this geometry +// array in the specified render bin is empty. +boolean isDlistUserSetEmpty(RenderBin renderBin) { + if (dlistUsers == null) + return true; + + HashSet raSet = dlistUsers.get(renderBin); + if (raSet == null) { + return true; + } + return raSet.isEmpty(); +} + +// This method is used for debugging only +int numDlistUsers(RenderBin renderBin) { + if (isDlistUserSetEmpty(renderBin)) + return 0; + + HashSet raSet = dlistUsers.get(renderBin); + return raSet.size(); +} + + 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_VERTEX_ATTR_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 and there are no + // vertex attrs + 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); + + } + geoBounds.getCenter(this.centroid); + } + + 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; + } + + // We will avoid merging geometry if there are any vertex attributes. + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + return false; + } + + // If intersect is allowed turn off merging + if (source.getCapability(Geometry.ALLOW_INTERSECT)) + return false; + + return true; + } + + @Override + void compile(CompileState compState) { + super.compile(compState); + + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + compState.needNormalsTransform = true; + } + } + + @Override + 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; + + if(morphUniverseList == null) { + morphUniverseList = new ArrayList(1); + morphUserLists = new ArrayList>(1); + } + synchronized (morphUniverseList) { + if (morphUniverseList.contains(m.universe)) { + index = morphUniverseList.indexOf(m.universe); + morphUserLists.get(index).add(m); + } else { + morphUniverseList.add(m.universe); + ArrayList 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; + + if(morphUniverseList == null) + return; + + synchronized (morphUniverseList) { + index = morphUniverseList.indexOf(m.universe); + ArrayList morphList = 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 = + ((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 = + ((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; + } + + + @Override + boolean intersect(Transform3D thisLocalToVworld, + Transform3D otherLocalToVworld, GeometryRetained geom) { + + Transform3D t3d = new Transform3D(); + boolean isIntersect = false; + + if (geom instanceof GeometryArrayRetained ) { + GeometryArrayRetained geomArray = (GeometryArrayRetained) geom; + + if (geomArray.validVertexCount >= validVertexCount) { + t3d.invert(otherLocalToVworld); + t3d.mul(thisLocalToVworld); + isIntersect = intersect(t3d, geom); + } else { + t3d.invert(thisLocalToVworld); + t3d.mul(otherLocalToVworld); + isIntersect = geomArray.intersect(t3d, this); + } + } else { + t3d.invert(thisLocalToVworld); + t3d.mul(otherLocalToVworld); + isIntersect = geom.intersect(t3d, this); + } + 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; // XXXX: 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) { + FloatBuffer texBuffer = (FloatBuffer)refTexCoordsBuffer[i].getROBuffer(); + 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; + } + + // NOTE: we don't need a getNumVertexAttrCount method, since getNum*Count + // is only called by Morph, which doesn't support vertex attrs + + + // 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(Utils.ptToSegSquare(center, coordinates[0], coordinates[1], iPnt)); + 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 0.0) + break; + } + + for (j=i; j 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] = Utils.ptToSegSquare(center, coordinates[0], coordinates[1], iPnt); + minDist = Utils.ptToSegSquare(center, coordinates[1], coordinates[2], minPnt); + if (minDist < dist[0]) { + dist[0] = minDist; + iPnt.x = minPnt.x; + iPnt.y = minPnt.y; + iPnt.z = minPnt.z; + } + minDist = Utils.ptToSegSquare(center, coordinates[2], coordinates[0], minPnt); + 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] = Utils.ptToSegSquare(center, coordinates[0], coordinates[1], iPnt); + minDist = Utils.ptToSegSquare(center, coordinates[1], coordinates[2], minPnt); + if (minDist < dist[0]) { + dist[0] = minDist; + iPnt.x = minPnt.x; + iPnt.y = minPnt.y; + iPnt.z = minPnt.z; + } + minDist = Utils.ptToSegSquare(center, coordinates[2], coordinates[3], minPnt); + if (minDist < dist[0]) { + dist[0] = minDist; + iPnt.x = minPnt.x; + iPnt.y = minPnt.y; + iPnt.z = minPnt.z; + } + + minDist = Utils.ptToSegSquare(center, coordinates[3], coordinates[0], minPnt); + 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]); + } + + @Override + 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) || + (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) && + bit == GeometryArray.ALLOW_VERTEX_ATTR_WRITE) || + (bit == GeometryArray.ALLOW_COUNT_WRITE)) { + mask = 1; + } + } + else { + if (bit == GeometryArray.ALLOW_REF_DATA_WRITE) + mask = 1; + } + if (mask != 0) { + setFrequencyChangeMask(bit, mask); + } + } + + int getTexCoordType() { + return texCoordType; + } + + int getVertexAttrType() { + return vertexAttrType; + } + +} diff --git a/src/javax/media/j3d/GeometryAtom.java b/src/javax/media/j3d/GeometryAtom.java new file mode 100644 index 0000000..a4351ef --- /dev/null +++ b/src/javax/media/j3d/GeometryAtom.java @@ -0,0 +1,263 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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(); + } + + @Override + public int getId() { + return nnuId; + } + + @Override + 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; + } + } + + @Override + public BoundingBox computeBoundingHull() { + /* + System.err.println("Bounds is " + source.vwcBounds); + for(int i=0; i= renderAtoms.length) { + RenderAtom[] newList = new RenderAtom[index + 1]; + System.arraycopy(renderAtoms, 0, newList, 0, renderAtoms.length); + renderAtoms = newList; + } + + RenderAtom ra = renderAtoms[index]; + if (ra == null) { + ra = new RenderAtom(); + renderAtoms[index] = ra; + ra.geometryAtom = this; + + // Allocate space based on number of geometry in the list + boolean isGeoTypeText3D = (geoType == GeometryRetained.GEO_TYPE_TEXT3D); + ra.rListInfo = new RenderAtomListInfo[geometryArray.length]; + for (int j = 0; j < ra.rListInfo.length; j++) { + ra.rListInfo[j] = new RenderAtomListInfo(ra, j); + if (isGeoTypeText3D) + ra.rListInfo[j].localToVworld = new Transform3D(); + } + } + return ra; + } +} + // If the renderAtom is transparent, then make sure that the + // value is up-to-date + + void updateCentroid() { + // New for 1.3.2 + // If the sortShape3DBounds flag is set, the bounds of the + // Shape3D node will be used in place of the computed + // GeometryArray bounds for transparency sorting for those + // Shape3D nodes whose boundsAutoCompute attribute is set to + // false. + if (VirtualUniverse.mc.sortShape3DBounds && + !source.boundsAutoCompute) { + + synchronized(lockObj) { + if (centroid == null) { + centroid = new Point3d[geometryArray.length]; + for (int j = 0; j < centroid.length; j++) { + centroid[j] = new Point3d(); + source.localBounds.getCenter(centroid[j]); + source.getCurrentLocalToVworld(0).transform(centroid[j]); + } + } + else { + for (int j = 0; j < centroid.length; j++) { + source.localBounds.getCenter(centroid[j]); + source.getCurrentLocalToVworld(0).transform(centroid[j]); + } + } + } + + return; + } + // End of new for 1.3.2 + + 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/javax/media/j3d/GeometryDecompressor.java b/src/javax/media/j3d/GeometryDecompressor.java new file mode 100644 index 0000000..7a5bfd8 --- /dev/null +++ b/src/javax/media/j3d/GeometryDecompressor.java @@ -0,0 +1,1217 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import javax.vecmath.Color4f; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +/** + * 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 ; + + @Override + 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< 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.err.println("struct {") ; + System.err.println(" double nx, ny, nz ;") ; + System.err.println("} gcNormals[65][65] = {"); + for (i = 0 ; i <= 64 ; i++) { + System.err.println("{") ; + for (j = 0 ; j <= 64 ; j++) { + if (j+i > 64) continue ; + System.err.println("{ " + gcNormals[i][j][0] + + ", " + gcNormals[i][j][1] + + ", " + gcNormals[i][j][2] + " }") ; + } + System.err.println("},") ; + } + System.err.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.err.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.err.print(" getBits(" + bitCount + ") " + d + ", " + + bitBufferCount + " available at gcIndex " + + gcIndex) ; + + if (bitCount == 0) { + if (debug) System.err.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.err.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.err.println("GeometryDecompressor.processSetState") ; + + ii = getBits(3, "bundling") ; + + bundlingNorm = ((currentHeader & 0x1) != 0) ; + bundlingColor = (((ii >>> 2) & 0x1) != 0) ; + doingAlpha = (((ii >>> 1) & 0x1) != 0) ; + + if (debug) + System.err.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.err.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.err.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.err.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.err.println(" absolute position: " + + curX + " " + curY + " " + curZ) ; + } else { + curX += dx ; curY += dy ; curZ += dz ; + if (debug) System.err.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.err.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.err.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.err.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.err.println(" absolute normal: sex " + curSex + + " oct " + curOct + + " u " + curU + " v " + curV) ; + else + System.err.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.err.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.err.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.err.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.err.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.err.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.err.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.err.println(" delta color: dr " + dr + + " dg " + dg + " db " + db + + " da " + da) ; + } + + // Do optional mesh buffer push. + if (mbp != 0) { + if (debug) + System.err.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.err.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.err.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.err.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.err.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.err.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.err.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.err.println("GeometryDecompressor.processEos") ; + } + + // Process a variable length no-op opcode. + private void processVNoop() { + int ii, ct ; + if (debug) System.err.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.err.println("GeometryDecompressor.processPassThrough") ; + + ignore = getBits(24, "passthrough") ; + ignore = getBits(32, "passthrough") ; + } + + // Process a skip-8 opcode. + private void processSkip8() { + int skip ; + if (debug) System.err.println("GeometryDecompressor.processSkip8") ; + + skip = getBits(8, "skip8") ; + } + + private void benchmarkStart(int length) { + vertexCount = 0 ; + System.err.println(" GeometryDecompressor: decompressing " + + length + " bytes...") ; + startTime = J3dClock.currentTimeMillis() ; + } + + private void benchmarkPrint(int length) { + float t = (J3dClock.currentTimeMillis() - startTime) / 1000.0f ; + System.err.println + (" done in " + t + " sec." + "\n" + + " decompressed " + vertexCount + " vertices at " + + (vertexCount/t) + " vertices/sec\n") ; + + System.err.print(" vertex data present: coords") ; + int floatVertexSize = 12 ; + if (bundlingNorm) { + System.err.print(" normals") ; + floatVertexSize += 12 ; + } + if (bundlingColor) { + System.err.println(" colors") ; + floatVertexSize += 12 ; + } + if (doingAlpha) { + System.err.println(" alpha") ; + floatVertexSize += 4 ; + } + System.err.println() ; + + System.err.println + (" bytes of data in generalized strip output: " + + (vertexCount * floatVertexSize) + "\n" + + " compression ratio: " + + (length / (float)(vertexCount * floatVertexSize)) + "\n") ; + } +} diff --git a/src/javax/media/j3d/GeometryDecompressorRetained.java b/src/javax/media/j3d/GeometryDecompressorRetained.java new file mode 100644 index 0000000..41ae770 --- /dev/null +++ b/src/javax/media/j3d/GeometryDecompressorRetained.java @@ -0,0 +1,404 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import javax.vecmath.Color4f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +/** + * 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). + // + // XXXX: 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. + */ + @Override + 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. + */ + @Override + 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.err.println("outputVertex: flag " + vertexReplaceCode) ; + System.err.println(" position " + position.toString()) ; + if (normal != null) + System.err.println(" normal " + normal.toString()) ; + if (color != null) + System.err.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. + */ + @Override + void outputColor(Color4f color) { + if (boundsOnly || positionsOnly) return ; + if (debug) System.err.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. + */ + @Override + void outputNormal(Vector3f normal) { + if (boundsOnly || positionsOnly) return ; + if (debug) System.err.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.err.println("\nGeometryDecompressorRetained") ; + + switch(bufferDataType) { + case TYPE_TRIANGLE: + System.err.println(" buffer TYPE_TRIANGLE") ; + break ; + case TYPE_LINE: + System.err.println(" buffer TYPE_LINE") ; + break ; + case TYPE_POINT: + System.err.println(" buffer TYPE_POINT") ; + break ; + default: + throw new IllegalArgumentException + (J3dI18N.getString("GeometryDecompressorRetained4")) ; + } + + System.err.print(" buffer data present: coords") ; + + if ((dataPresent & CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0) + System.err.print(" normals") ; + if ((dataPresent & CompressedGeometryHeader.COLOR_IN_BUFFER) != 0) + System.err.print(" colors") ; + if ((dataPresent & CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0) + System.err.print(" alpha") ; + + System.err.println() ; + if (boundsOnly) System.err.println(" computing bounds only") ; + if (positionsOnly) System.err.println(" computing positions only") ; + + startTime = J3dClock.currentTimeMillis() ; + } + + private void endPrint() { + endTime = J3dClock.currentTimeMillis() ; + + if (benchmark || statistics) + printBench() ; + + if (statistics) + printStats() ; + } + + private void printBench() { + float t = (endTime - startTime) / 1000.0f ; + + if (boundsOnly) { + System.err.println(" decompression took " + t + " sec.\n") ; + return ; + } + + System.err.println + (" decompression + strip conversion took " + t + " sec.") ; + + switch(bufferDataType) { + case TYPE_POINT: + System.err.println + (" decompressed " + (vlist.size()) + + " points at " + (vlist.size()/t) + + " points/sec.\n") ; + break ; + case TYPE_LINE: + System.err.println + (" decompressed " + (vlist.vertexCount - vlist.stripCount) + + " lines at " + ((vlist.vertexCount - vlist.stripCount)/t) + + " lines/sec.\n") ; + break ; + case TYPE_TRIANGLE: + System.err.println + (" decompressed " + + (vlist.vertexCount - 2*vlist.stripCount) + + " triangles at " + + ((vlist.vertexCount - 2*vlist.stripCount)/t) + + " triangles/sec.\n") ; + break ; + } + } + + private void printStats() { + System.err.println(" bounding box:\n lower " + lbounds.toString() + + "\n upper " + ubounds.toString()) ; + + if (boundsOnly) return ; + + System.err.print + (" number of vertices in GeometryArray output: " + + vlist.vertexCount + "\n" + + " GeometryArray vertex data present: coords") ; + + if ((vlist.vertexFormat & GeometryArray.NORMALS) != 0) + System.err.print(" normals") ; + + if ((vlist.vertexFormat & GeometryArray.COLOR) != 0) + System.err.print(" colors") ; + + if ((vlist.vertexFormat & GeometryArray.WITH_ALPHA) != 0) + System.err.print(" alpha") ; + + System.err.println("\n number of strips: " + vlist.stripCount) ; + if (vlist.stripCount > 0) + System.err.println + (" vertices/strip: " + + ((float)vlist.vertexCount / (float)vlist.stripCount)) ; + } +} diff --git a/src/javax/media/j3d/GeometryDecompressorShape3D.java b/src/javax/media/j3d/GeometryDecompressorShape3D.java new file mode 100644 index 0000000..4854b8c --- /dev/null +++ b/src/javax/media/j3d/GeometryDecompressorShape3D.java @@ -0,0 +1,487 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import java.util.ArrayList; + +import javax.vecmath.Color4f; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +/** + * 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. + */ + @Override + 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. + */ + @Override + void outputVertex(Point3f position, Vector3f normal, + Color4f color, int vertexReplaceCode) { + + if (curNormal != null) normal = curNormal ; + vlist.addVertex(position, normal, color, vertexReplaceCode) ; + + if (debug) { + System.err.println(" outputVertex: flag " + vertexReplaceCode) ; + System.err.println(" position " + position.toString()) ; + if (normal != null) + System.err.println(" normal " + normal.toString()) ; + if (color != null) + System.err.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. + */ + @Override + void outputColor(Color4f color) { + if (debug) System.err.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. + */ + @Override + void outputNormal(Vector3f normal) { + if (debug) System.err.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.err.println("\nGeometryDecompressorShape3D") ; + + switch(bufferDataType) { + case TYPE_TRIANGLE: + System.err.println(" buffer TYPE_TRIANGLE") ; + break ; + case TYPE_LINE: + System.err.println(" buffer TYPE_LINE") ; + break ; + case TYPE_POINT: + System.err.println(" buffer TYPE_POINT") ; + break ; + default: + throw new IllegalArgumentException + (J3dI18N.getString("GeometryDecompressorShape3D1")) ; + } + + System.err.print(" buffer data present: coords") ; + + if ((dataPresent & CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0) + System.err.print(" normals") ; + if ((dataPresent & CompressedGeometryHeader.COLOR_IN_BUFFER) != 0) + System.err.print(" colors") ; + if ((dataPresent & CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0) + System.err.print(" alpha") ; + + System.err.println() ; + + stripCount = 0 ; + vertexCount = 0 ; + triangleCount = 0 ; + origVertexCount = 0 ; + + startTime = J3dClock.currentTimeMillis() ; + } + + private void endPrint() { + endTime = J3dClock.currentTimeMillis() ; + + if (benchmark || statistics) + printBench() ; + + if (statistics) + printStats() ; + } + + private void printBench() { + float t = (endTime - startTime) / 1000.0f ; + System.err.println + (" decompression + strip conversion took " + t + " sec.") ; + + switch(bufferDataType) { + case TYPE_POINT: + System.err.println + (" points decompressed: " + vertexCount + "\n" + + " net decompression rate: " + (vertexCount/t) + + " points/sec.\n") ; + break ; + case TYPE_LINE: + System.err.println + (" lines decompressed: " + (vertexCount - stripCount) + "\n" + + " net decompression rate: " + ((vertexCount - stripCount)/t) + + " lines/sec.\n") ; + break ; + case TYPE_TRIANGLE: + System.err.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.err.println(" using individual triangle output") ; + break ; + case TRI_STRIP_SET: + System.err.println(" using strip output") ; + break ; + case TRI_STRIP_AND_FAN_SET: + System.err.println(" using strips and fans for output") ; + break ; + case TRI_STRIP_AND_TRI_SET: + System.err.println(" using strips and triangles for output") ; + break ; + } + + System.err.print + (" number of Shape3D objects: " + shapes.size() + + "\n number of Shape3D decompressed vertices: ") ; + + if (triOutputType == TRI_SET || bufferDataType == TYPE_POINT) { + System.err.println(vertexCount) ; + } + else if (triOutputType == TRI_STRIP_AND_TRI_SET) { + System.err.println((vertexCount + triangleCount*3) + + "\n number of strips: " + stripCount + + "\n number of individual triangles: " + + triangleCount) ; + if (stripCount > 0) + System.err.println + (" vertices/strip: " + (float)vertexCount/stripCount + + "\n triangles represented in strips: " + + (vertexCount - 2*stripCount)) ; + } + else { + System.err.println(vertexCount + + "\n number of strips: " + stripCount) ; + if (stripCount > 0) + System.err.println + (" vertices/strip: " + (float)vertexCount/stripCount) ; + } + + System.err.print(" vertex data present in last Shape3D: coords") ; + if ((vlist.vertexFormat & GeometryArray.NORMALS) != 0) + System.err.print(" normals") ; + + if ((vlist.vertexFormat & GeometryArray.COLOR) != 0) { + System.err.print(" colors") ; + if ((vlist.vertexFormat & GeometryArray.WITH_ALPHA) != 0) + System.err.print(" alpha") ; + } + System.err.println() ; + } +} + diff --git a/src/javax/media/j3d/GeometryLock.java b/src/javax/media/j3d/GeometryLock.java new file mode 100644 index 0000000..bb1132c --- /dev/null +++ b/src/javax/media/j3d/GeometryLock.java @@ -0,0 +1,85 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +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/javax/media/j3d/GeometryRetained.java b/src/javax/media/j3d/GeometryRetained.java new file mode 100644 index 0000000..7f9fded --- /dev/null +++ b/src/javax/media/j3d/GeometryRetained.java @@ -0,0 +1,387 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Point3d; + +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) { + 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)) { + int index = universeList.indexOf(s.universe); + userLists.get(index).add(s); + } else { + universeList.add(s.universe); + ArrayList 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) { + if (s.sourceNode.boundsAutoCompute) { + decrComputeGeoBounds(); + } + + if (this instanceof GeometryArrayRetained) { + if (((GeometryArrayRetained)this).isWriteStatic()) { + return; + } + } + + synchronized (universeList) { + int index = universeList.indexOf(s.universe); + ArrayList shapeList = userLists.get(index); + shapeList.remove(s); + if (shapeList.size() == 0) { + userLists.remove(index); + universeList.remove(index); + } + } + + } + + public void updateObject() { + this.update(); + } + + + abstract void computeBoundingBox(); + + @Override + void setLive(boolean inBackgroundGroup, int refCount) { + doSetLive(inBackgroundGroup,refCount); + super.markAsLive(); + } + + /** + * This setLive routine calls the superclass's method when reference + * count is 1 + */ + @Override + 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, + int screen, boolean ignoreVertexColors); + + /** + * 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 ; + } + + // Issue 199 -- Chien + abstract boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex); + + // Old stuff -- Chien + //abstract boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo, int flags, Point3d iPnt); + + abstract boolean intersect(Bounds targetBound); + abstract boolean intersect(Point3d[] pnts); + abstract boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom); + + void storeInterestData(PickInfo pickInfo, int flags, GeometryRetained geom, int geomIndex, + int[] vtxIndexArr, Point3d iPnt, double dist) { + + PickInfo.IntersectionInfo iInfo = null; + + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + PickInfo.IntersectionInfo iInfoArr[] = pickInfo.getIntersectionInfos(); + if((iInfoArr == null) || (iInfoArr.length == 0)) { + iInfo = pickInfo.createIntersectionInfo(); + pickInfo.insertIntersectionInfo(iInfo); + } + else { + assert(iInfoArr.length == 1); + iInfo = iInfoArr[0]; + } + } + else if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + iInfo = pickInfo.createIntersectionInfo(); + pickInfo.insertIntersectionInfo(iInfo); + } + else { + assert(false); + } + // This only set the reference to geometry.source. + iInfo.setGeometry((Geometry) geom.source); + // The rest are by copy. + iInfo.setGeometryIndex(geomIndex); + iInfo.setDistance(dist); + iInfo.setIntersectionPoint(iPnt); + iInfo.setVertexIndices(vtxIndexArr); + } + + boolean intersect(Transform3D thisLocalToVworld, + Transform3D otherLocalToVworld, GeometryRetained geom) { + Transform3D t3d = new Transform3D(); + t3d.invert(otherLocalToVworld); + t3d.mul(thisLocalToVworld); + return intersect(t3d, geom); + } + + + boolean intersect(Transform3D thisLocalToVworld, Bounds targetBound) { + Bounds transBound = (Bounds) targetBound.clone(); + + Transform3D t3d = new Transform3D(); + t3d.invert(thisLocalToVworld); + transBound.transform(t3d); + return intersect(transBound); + } + + + // Return a flag indicating whether or not this Geometry object can be in + // a display list. + // + // XXXX: Note that for IndexedGeometryArray objects, the original + // vertex format is used in making this determination, even when it has + // been unindexified. This should be fixed by using the vertex format of + // the mirror geometry if there is one. + boolean canBeInDisplayList(boolean alphaEditable) { + // Check global flag to see whether we can build display lists + if (!VirtualUniverse.mc.isDisplayList) { + return false; + } + + // Can't build display lists if geometry is frequently writable + // + // Issue 181 : to fix performance regression from 1.3.2, we will allow + // editable geometry if the optimizeForSpace property is set false and + // the cachedChangedFrequent flag is set; note this will basically + // restore the 1.3.2 behavior, which isn't completely correct. + // Eventually, we should fix the bug that is causing the + // cachedChangedFrequent bit to be wrong; we can then remove the + // erroneous dependency on optimizeForSpace. + if (this.isEditable) { + if (cachedChangedFrequent != 0) { + return false; + } + + // TODO: remove the following when cachedChangedFrequent is fixed + // to correctly reflect the state + if (!VirtualUniverse.mc.buildDisplayListIfPossible) { + return false; + } + } + + if (this instanceof GeometryArrayRetained) { + int vFormat = ((GeometryArrayRetained)this).vertexFormat; + + // If geometry has vertex attributes, check whether + // vertex attributes are allowed in display lists + if (((vFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) && + !VirtualUniverse.mc.vertexAttrsInDisplayList) { + return false; + } + + // Can't build display lists if alpha is editable and + // geometry array has colors + if (alphaEditable && ((vFormat & GeometryArray.COLOR) != 0)) { + return false; + } + + // Only build DL for by-ref geometry when system property is set. + // Exclude NIO buffers and use-coord-index-only + if ((vFormat & GeometryArray.BY_REFERENCE) != 0) { + if (!VirtualUniverse.mc.buildDisplayListIfPossible) { + return false; + } + + // XXXX: we could change this to allow display lists for + // non-interleaved NIO buffers, but we would first need to + // update the now-obsolete buildGAForBuffer method to handle + // vertex attrs + if ((vFormat & GeometryArray.USE_NIO_BUFFER) != 0) { + return false; + } + + if ((vFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) { + return false; + } + } + + return true; + } else { + // Can't build display lists for other kind of geometry + // NOTE: This method is not called for any type of Geometry + // other than GeometryArray, so we shouldn't even get here. + return false; + } + } + +void computeCentroid() { + geoBounds.getCenter(this.centroid); +} + + abstract int getClassType(); + +} diff --git a/src/javax/media/j3d/GeometryStripArray.java b/src/javax/media/j3d/GeometryStripArray.java new file mode 100644 index 0000000..02db947 --- /dev/null +++ b/src/javax/media/j3d/GeometryStripArray.java @@ -0,0 +1,268 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @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 + * validVertexCount > vertexCount + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @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 + * validVertexCount > vertexCount + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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); + } + + /** + * Constructs an empty GeometryStripArray object with the + * specified number of vertices, vertex format, number of texture + * coordinate sets, texture coordinate mapping array, vertex + * attribute count, vertex attribute sizes array, and array of + * per-strip vertex counts. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * validVertexCount > vertexCount + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public GeometryStripArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int[] stripVertexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + ((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: + *

    + * initialVertexIndex + validVertexCount > vertexCount,
    + * initialCoordIndex + validVertexCount > vertexCount,
    + * initialColorIndex + validVertexCount > vertexCount,
    + * initialNormalIndex + validVertexCount > vertexCount,
    + * initialTexCoordIndex + validVertexCount > vertexCount + *
+ *

+ * + * @exception ArrayIndexOutOfBoundsException if the geometry data format + * is BY_REFERENCE and any the following + * are true for non-null array references: + *

    + * CoordRef.length < num_words * + * (initialCoordIndex + validVertexCount)
    + * ColorRef.length < num_words * + * (initialColorIndex + validVertexCount)
    + * NormalRef.length < num_words * + * (initialNormalIndex + validVertexCount)
    + * TexCoordRef.length < num_words * + * (initialTexCoordIndex + validVertexCount)
    + *
+ * where num_words depends on which variant of + * setArrayRef 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 + */ + @Override + public void setValidVertexCount(int validVertexCount) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/javax/media/j3d/GeometryStripArrayRetained.java b/src/javax/media/j3d/GeometryStripArrayRetained.java new file mode 100644 index 0000000..f08c9e9 --- /dev/null +++ b/src/javax/media/j3d/GeometryStripArrayRetained.java @@ -0,0 +1,828 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.nio.FloatBuffer; +import java.util.ArrayList; + +import javax.vecmath.TexCoord2f; +import javax.vecmath.TexCoord3f; + + +/** + * 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")); + } + if ((initialCoordIndex + total) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray7")); + } + if ((initialColorIndex + total) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray4")); + } + if ((initialNormalIndex + total) > vertexCount) { + throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray5")); + } + 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")); + } + } + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) { + for (i = 0; i < vertexAttrCount; i++) { + if ((initialVertexAttrIndex[i] + total) > vertexCount) { + throw new IllegalArgumentException( + J3dI18N.getString("GeometryStripArray8")); + } + } + } + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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 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) { + 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.err.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() { + // Issue 353: set all elements to null so we don't leak + // NOTE: we really should change this to be an ArrayList, but that + // would be a less localized change. Consider for 1.6.0. + for (int i = 0; i < bhNodeCount; i++) { + bhNodeArr[i] = null; + } + + 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; + + clearBhNodeArr(); + + // System.err.println("GS : nodes.length is " + nodes.length); + + for (int i=0; i=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; + } + + int index = getBHTreeIndex(((BHLeafNode) bhNodeArr[0]).getLocale()); + if (index < 0) { + // Issue 353: must clear array after we are done with it + clearBhNodeArr(); + return; + } + BHTree currTree = bhTreeArr[index]; + currTree.delete(bhNodeArr, bhNodeCount); + + // Issue 353: must clear array after we are done with it + clearBhNodeArr(); + + // 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); + } + + // Issue 353: must clear array after we are done with it + clearBhNodeArr(); + + } + + 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); + + } + + // Issue 353: must clear array after we are done with it + clearBhNodeArr(); + + } + + // 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, + BoundingBox frustumBBox, + Locale locale, long referenceTime, + boolean stateChanged, + int visibilityPolicy) { + + int i, j; + boolean unviInFB = true; + + // System.err.println("GeometryStructure : view's locale is " + locale); + lock.readLock(); + + // Issue 353: create a new array list each time rather than passing it + // in. This will not generate too much garbage, since we only call + // this once per frame and it is very short-lived. + ArrayList bhTrees = new ArrayList(); + if (bhTreeCount == 1) { + // For debugging only. + if (J3dDebug.devPhase) { + if (J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2)) { + System.err.println("GeometryStructure : In simple case"); + System.err.println("GeometryStructure : view's locale is " + + locale); + System.err.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=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; iupdateData 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. + *
+ * NOTE: Applications should not call this method directly. + * + * @param geometry the Geometry object being updated. + * @see GeometryArray#updateData + */ + public void updateData(Geometry geometry); +} diff --git a/src/javax/media/j3d/GraphStructureChangeListener.java b/src/javax/media/j3d/GraphStructureChangeListener.java new file mode 100644 index 0000000..ff440cd --- /dev/null +++ b/src/javax/media/j3d/GraphStructureChangeListener.java @@ -0,0 +1,68 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Listener interface for monitoring structural changes to live scene + * graphs. BranchGroup additions, removals and moves are reported. + * + * @see VirtualUniverse#addGraphStructureChangeListener + * + * @since Java 3D 1.4 + */ +public interface GraphStructureChangeListener { + /** + * Invoked when a branch group is added. + * Called just before the child is added to the parent. + * Parent can be either a BranchGroup or a Locale. + * + * @param parent the parent of the child being added + * @param child the child being added + */ + public void branchGroupAdded(Object parent, BranchGroup child); + + /** + * Invoked when a branch group is removed. + * Called just after the child has been removed from the parent. + * Parent can be either a BranchGroup or a Locale. + * + * @param parent the parent of the child being added + * @param child the child being added + */ + public void branchGroupRemoved(Object parent, BranchGroup child); + + /** + * Invoked when a branch group is moved. + * Called after a child has been moved to it's new parent. This call differs + * from the other methods in that the child is live when this method is called. + * + * @param oldParent the original parent of the child being moved + * @param newParent the new parent of the child being moved + * @param child the child being moved + */ + public void branchGroupMoved(Object oldParent, Object newParent, BranchGroup child); +} diff --git a/src/javax/media/j3d/GraphicsConfigInfo.java b/src/javax/media/j3d/GraphicsConfigInfo.java new file mode 100644 index 0000000..8483b83 --- /dev/null +++ b/src/javax/media/j3d/GraphicsConfigInfo.java @@ -0,0 +1,58 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Container for the GraphicsTemplate3D and other private data about a selected + * GraphicsConfiguration. An instance of this class is created along with an + * instance of GrahpicsConfiguration, whenever getBestConfiguration is called. + */ +class GraphicsConfigInfo { + private GraphicsConfigTemplate3D graphicsConfigTemplate3D = null; + private Object privateData = null; + + GraphicsConfigInfo(GraphicsConfigTemplate3D graphicsConfigTemplate3D) { + setGraphicsConfigTemplate3D(graphicsConfigTemplate3D); + } + + GraphicsConfigTemplate3D getGraphicsConfigTemplate3D() { + return graphicsConfigTemplate3D; + } + + void setGraphicsConfigTemplate3D(GraphicsConfigTemplate3D graphicsConfigTemplate3D) { + this.graphicsConfigTemplate3D = graphicsConfigTemplate3D; + } + + Object getPrivateData() { + return privateData; + } + + void setPrivateData(Object privateData) { + this.privateData = privateData; + } + +} diff --git a/src/javax/media/j3d/GraphicsConfigTemplate3D.java b/src/javax/media/j3d/GraphicsConfigTemplate3D.java new file mode 100644 index 0000000..757f5a6 --- /dev/null +++ b/src/javax/media/j3d/GraphicsConfigTemplate3D.java @@ -0,0 +1,446 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.GraphicsConfigTemplate; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; + +/** + * 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; + int stencilSize; + + // Temporary variables use for passing argument to/from Request Renderer + Object testCfg; + + static Object globalLock = new Object(); + static Object monitorLock = new Object(); + static volatile boolean threadWaiting = false; + + /** + * Constructs a GraphicsConfigTemplate3D object with default parameters. + * The default values are as follows: + *
    + * depthSize : 16
    + * doubleBuffer : REQUIRED
    + * sceneAntialiasing : UNNECESSARY
    + * stereo : UNNECESSARY
    + * redSize : 2
    + * greenSize : 2
    + * blueSize : 2
    + * stencilSize : 0
    + *
+ */ + public GraphicsConfigTemplate3D() { + doubleBuffer = REQUIRED; + stereo = UNNECESSARY; + depthSize = 16; + stencilSize = 0; + 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 stencil 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 + * + * @since Java 3D 1.4 + */ + public void setStencilSize(int value) { + if (value < 0) + return; + + stencilSize = value; + } + + /** + * Retrieves the size of the stencil buffer. + * + * @return the current value of the stencilSize attribute + * + * @since Java 3D 1.4 + */ + public int getStencilSize() { + return stencilSize; + } + + /** + * 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 + */ + @Override + 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 true if this GraphicsConfiguration object + * can be used to create surfaces that can be rendered to, + * false if the GraphicsConfiguration can not be used + * to create a drawing surface usable by this API. + */ + @Override + 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: + // Issue 279 - loop until ready + while (threadWaiting) { + try { + monitorLock.wait(); + } catch (InterruptedException e) { + System.err.println(e); + } + } + break; + case J3dThread.NOTIFY: + monitorLock.notify(); + threadWaiting = false; + break; + } + } + } + + + // Return a string representing the value, one of: + // REQUIRED, PREFERRED, or UNNECESSARY + private static final String enumStr(int val) { + switch (val) { + case REQUIRED: + return "REQUIRED"; + case PREFERRED: + return "PREFERRED"; + case UNNECESSARY: + return "UNNECESSARY"; + } + + return "UNDEFINED"; + } + + /** + * Returns a string representation of this object. + * @return a string representation of this object. + */ + @Override + public String toString() { + return + "redSize : " + redSize + ", " + + "greenSize : " + greenSize + ", " + + "blueSize : " + blueSize + ", " + + "depthSize : " + depthSize + ", " + + "doubleBuffer : " + enumStr(doubleBuffer) + ", " + + "sceneAntialiasing : " + enumStr(sceneAntialiasing) + ", " + + "stereo : " + enumStr(stereo); + } +} diff --git a/src/javax/media/j3d/GraphicsContext3D.java b/src/javax/media/j3d/GraphicsContext3D.java new file mode 100644 index 0000000..75b2d1d --- /dev/null +++ b/src/javax/media/j3d/GraphicsContext3D.java @@ -0,0 +1,3068 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Dimension; +import java.awt.Point; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Color3f; +import javax.vecmath.Vector3d; + +/** + * 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: + *
    + *
  • Background object: null
  • + *
  • Fog object: null
  • + *
  • ModelClip object: null
  • + *
  • Appearance object: null
  • + *
  • List of Light objects: empty
  • + *
  • high-res coordinate: (0, 0, 0)
  • + *
  • modelTransform: identity
  • + *
  • AuralAttributes object: null
  • + *
  • List of Sound objects: empty
  • + *
  • buffer override: false
  • + *
  • front buffer rendering: false
  • + *
  • stereo mode: STEREO_BOTH
  • + *
+ * + *

+ * 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 + * flush(boolean) + * method to ensure that the rendering actually happens. The flush + * method is implicitly called in the following cases: + * + *

    + *
  • The readRaster method calls + * flush(true)
  • + *
  • The Canvas3D.swap method calls + * flush(true)
  • + *
  • The Java 3D renderer calls flush(true) prior to + * swapping the buffer for a double buffered on-screen Canvas3D
  • + *
  • The Java 3D renderer calls flush(true) prior to + * copying into the off-screen buffer of an off-screen Canvas3D
  • + *
  • The Java 3D renderer calls flush(false) after + * calling the preRender, renderField, postRender, and postSwap + * Canvas3D callback methods.
  • + *
+ * + *

+ * 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; + +// +// Graphics state +// +// current user specified graphics state + private Background uBackground = null; + private Fog uFog = null; + private Appearance uAppearance = null; + private Vector uLights = new Vector(); + private HiResCoord uHiRes = new HiResCoord(); + private Vector uSounds = new Vector(); + private AuralAttributes uAuralAttributes = null; + private boolean uBufferOverride = false; + private boolean uFrontBufferRendering = false; + private int uStereoMode = STEREO_BOTH; + private 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 + // XXXX: 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 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 DISPOSE2D = 26; + static final int NCOMMANDS = 27; // needs to be incremented + // when a new command is to be + // added to the list + + private static Integer[] commands = new Integer[NCOMMANDS]; + private static Integer[] stereoModes = { + new Integer(STEREO_LEFT), + new Integer(STEREO_RIGHT), + new Integer(STEREO_BOTH) + }; + + // dirty bits + private static final int BUFFER_MODE = 0x1; + private int dirtyMask = 0; + + // multi-texture + private int numActiveTexUnit = 0; + private int lastActiveTexUnitIndex = 0; + + // for read raster + private volatile boolean readRasterReady = false; + + // for runMonitor + private boolean gcReady = false; + private 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 + * + * @exception IllegalSharingException if the specified appearance refers + * to an ImageComponent2D that is being used by a Canvas3D as + * an off-screen buffer. + */ + public void setAppearance(Appearance appearance) { + + if(appearance == null) { + if(defaultAppearance == null) { + defaultAppearance = new Appearance(); + } + appearance = defaultAppearance; + } else { + // Check whether any ImageComponent2D referred to by + // the new appearance is being used as an off-screen buffer and throw + // IllegalSharingException if it is. + TextureRetained texRetained; + ImageComponent[] images; + AppearanceRetained appRetained = (AppearanceRetained)appearance.retained; + if(appRetained.texture != null) { + assert (appRetained.texUnitState == null); + texRetained = appRetained.texture; + images = texRetained.getImages(); + if(images != null) { + for(int i=0; i 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.add(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(FogRetained fogRet) { + // Issue 144: update localToVWorldScale for all types of Fog + fogRet.setLocalToVworldScale(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 = new Transform3D(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 = new Transform3D(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.set(index, sound); + 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 = this.sounds.get(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.set(index, sound); + 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.add(index, sound); + 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.add(index, sound); + 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.remove(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 = this.sounds.get(index); + SoundScheduler soundScheduler = getSoundScheduler(); + ((SoundRetained)(sound.retained)).setInImmCtx(false); + this.sounds.remove(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) { + return uSounds.get(index); +} + +/** + * 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.add(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.add(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) { + // XXXX: 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); + // XXXX (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); + // XXXX (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 = this.sounds.get(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 + * frontBufferRendering and stereoMode + * 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 + * bufferOverride flag is enabled. + *

+ * 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 + * bufferOverride flag is enabled. + *

    + *
  • + * STEREO_LEFT specifies that rendering is done into + * the left eye. + *
  • + *
  • + * STEREO_RIGHT specifies that rendering is done into + * the right eye. + *
  • + *
  • + * STEREO_BOTH specifies that rendering is done into + * both eyes. This is the default. + *
  • + *
+ * + *

+ * 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 STEREO_LEFT, + * STEREO_RIGHT, or STEREO_BOTH. + * + * @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; + + // XXXX: 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 { + // Issue 78 - need to get the drawingSurface info every + // frame; this is necessary since the HDC (window ID) + // on Windows can become invalidated without our + // being notified! + if (!canvas3d.offScreen) { + canvas3d.drawingSurfaceObject.getDrawingSurfaceObjectInfo(); + } + + if (canvas3d.drawingSurfaceObject.renderLock()) { + // XXXX : Fix texture + /* + if (canvas3d.useSharedCtx) { + if (canvas3d.screen.renderer.sharedCtx == 0) { + synchronized (VirtualUniverse.mc.contextCreationLock) { + canvas3d.screen.renderer.sharedCtx = canvas3d.createNewContext( + canvas3d.screen.display, + canvas3d.window, 0, true, + canvas3d.offScreen); + canvas3d.screen.renderer.sharedCtxTimeStamp = + VirtualUniverse.mc.getContextTimeStamp(); + canvas3d.screen.renderer.needToRebuildDisplayList = true; + } + } + } + */ + + if (canvas3d.ctx == null) { + synchronized (VirtualUniverse.mc.contextCreationLock) { + canvas3d.ctx = canvas3d.createNewContext(null, false); + if (canvas3d.ctx == null) { + canvas3d.drawingSurfaceObject.unLock(); + return; + } + + canvas3d.ctxTimeStamp = + VirtualUniverse.mc.getContextTimeStamp(); + canvas3d.screen.renderer.listOfCtxs.add(canvas3d.ctx); + canvas3d.screen.renderer.listOfCanvases.add(canvas3d); + + if (canvas3d.graphics2D != null) { + canvas3d.graphics2D.init(); + } + + // enable separate specular color + canvas3d.enableSeparateSpecularColor(); + } + + // create the cache texture state in canvas + // for state download checking purpose + if (canvas3d.texUnitState == null) { + canvas3d.createTexUnitState(); + } + + canvas3d.drawingSurfaceObject.contextValidated(); + canvas3d.screen.renderer.currentCtx = canvas3d.ctx; + canvas3d.screen.renderer.currentDrawable = canvas3d.drawable; + 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; jflush(true) prior to reading the + * frame buffer. + * + * @param raster the Raster object used to read the + * contents of the frame buffer + * + * @exception IllegalArgumentException if the image class of the specified + * Raster's ImageComponent2D is not ImageClass.BUFFERED_IMAGE. + * + * @exception IllegalArgumentException if the specified Raster's + * ImageComponent2D is in by-reference mode and its + * RenderedImage is null. + * + * @exception IllegalArgumentException if the the Raster's + * ImageComponent2D format + * is not a 3-component format (e.g., FORMAT_RGB) + * or a 4-component format (e.g., FORMAT_RGBA). + * + * @exception IllegalSharingException if the Raster object is + * part of a live scene graph, or if the Raster's ImageComponent2D is + * part of a live scene graph. + * + * @exception IllegalSharingException if the Raster's ImageComponent2D is + * being used by an immediate mode context, or by a Canvas3D as + * an off-screen buffer. + * + * @see #flush + * @see ImageComponent + * @see DepthComponent + */ + public void readRaster(Raster raster) { + if ((raster != null) && raster.isLive()) { + ImageComponent2D image = raster.getImage(); + if (image != null){ + ImageComponent2DRetained imageRetained = (ImageComponent2DRetained)image.retained; + if (image.getImageClass() != ImageComponent.ImageClass.BUFFERED_IMAGE) { + throw new IllegalArgumentException(J3dI18N.getString("GraphicsContext3D33")); + } + if (image.isByReference() && (image.getImage() == null)) { + throw new IllegalArgumentException(J3dI18N.getString("GraphicsContext3D34")); + } + if (imageRetained.getNumberOfComponents() < 3) { + throw new IllegalArgumentException(J3dI18N.getString("GraphicsContext3D35")); + } + if (image.isLive()) { + throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D36")); + } + if (imageRetained.getInImmCtx() || imageRetained.getUsedByOffScreen()) { + throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D37")); + } + } + } + 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(); + Dimension rasterSize = new Dimension(); + ImageComponent2DRetained image = ras.image; + + int format = 0; // Not use in case of DepthComponent read + + if (canvas3d.ctx == null) { + // Force an initial clear if one has not yet been done + doClear(); + } + + if (J3dDebug.devPhase && J3dDebug.debug) { + J3dDebug.doAssert(canvas3d.ctx != null, "canvas3d.ctx != null"); + } + + ras.getSize(rasterSize); + // allocate read buffer space + if ( (ras.type & Raster.RASTER_COLOR) != 0) { + if ((rasterSize.width > ras.image.width) || + (rasterSize.height > ras.image.height)) { + throw new RuntimeException(J3dI18N.getString("GraphicsContext3D27")); + } + } + + 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 ((rasterSize.width > ras.depthComponent.width) || + (rasterSize.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 (image.isByReference()) { + image.geomLock.getLock(); + image.evaluateExtensions(canvas3d); + image.geomLock.unLock(); + } else { + // If image has a null buffer ( BufferedImage) + if (image.imageData == null) { + image.createBlankImageData(); + } + // Check for possible format conversion in imageData + else { + // Format convert imageData if format is unsupported. + image.evaluateExtensions(canvas3d); + } + } + } + + // 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); + Point rasterSrcOffset = new Point(); + ras.getSrcOffset(rasterSrcOffset); + + int depthType = 0; + Object depthBuffer = null; + if (ras.depthComponent != null) { + depthType = ras.depthComponent.type; + if (depthType == DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT) + depthBuffer = floatBuffer; + else + depthBuffer = intBuffer; + } + + int imageDataType = 0; + int imageFormatType = 0; + Object imageBuffer = null; + + if ( (ras.type & Raster.RASTER_COLOR) != 0) { + imageDataType = image.getImageDataTypeIntValue(); + imageFormatType = image.getImageFormatTypeIntValue(false); + imageBuffer = image.imageData.get(); + } + + Pipeline.getPipeline().readRaster(canvas3d.ctx, + ras.type, rasterSrcOffset.x, rasterSrcOffset.y, + rasterSize.width, rasterSize.height, canvasSize.height, + imageDataType, + imageFormatType, + imageBuffer, + depthType, depthBuffer); + + canvas3d.drawingSurfaceObject.unLock(); + } + } catch (NullPointerException ne) { + canvas3d.drawingSurfaceObject.unLock(); + throw ne; + } + + if ( (ras.type & Raster.RASTER_DEPTH) != 0) { + if (ras.depthComponent.type == + DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT) + ((DepthComponentFloatRetained)ras.depthComponent).retrieveDepth( + floatBuffer, rasterSize.width, rasterSize.height); + else if (ras.depthComponent.type == + DepthComponentRetained.DEPTH_COMPONENT_TYPE_INT) + ((DepthComponentIntRetained)ras.depthComponent).retrieveDepth( + intBuffer, rasterSize.width, rasterSize.height); + else if (ras.depthComponent.type == + DepthComponentRetained.DEPTH_COMPONENT_TYPE_NATIVE) + ((DepthComponentNativeRetained)ras.depthComponent).retrieveDepth( + intBuffer, rasterSize.width, rasterSize.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); + } + // Issue 131: AutomaticOffscreen canvases must be treated as onscreen ones. + if (wait && canvas3d.active && canvas3d.isRunningStatus && + !canvas3d.manualRendering ) { + // 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)(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.setSceneAmbient(canvas3d.ctx, sceneAmbient.x, + sceneAmbient.y, sceneAmbient.z); + + canvas3d.canvasDirty |= Canvas3D.AMBIENTLIGHT_DIRTY; + canvas3d.sceneAmbient.set(sceneAmbient); + + if (canvas3d.enableMask != enableMask) { + canvas3d.canvasDirty |= Canvas3D.LIGHTENABLES_DIRTY; + // XXXX: 32 => renderBin.maxLights + canvas3d.setLightEnables(canvas3d.ctx, enableMask, 32); + canvas3d.enableMask = enableMask; + } + + // Force LightBin.updateAttributes and EnvironmentSet.updateAttributes + // to use the within frame case. + canvas3d.lightBin = null; + 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; + } + } + + // Force EnvironmentSet.updateAttributes to use the within frame case. + canvas3d.environmentSet = null; + + } + + + + boolean updateState(RenderBin rb, int geometryType) { + + boolean useAlpha = false; + 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; + } + } + + // Set flag indicating whether we are using shaders + boolean useShaders = false; + if (app instanceof ShaderAppearanceRetained) { + ShaderProgramRetained spR = ((ShaderAppearanceRetained)app).shaderProgram; + if ( spR != null) { + spR.updateNative(canvas3d, true); + + ShaderAttributeSetRetained sasR = + ((ShaderAppearanceRetained)app).shaderAttributeSet; + + if (sasR != null) { + sasR.updateNative(canvas3d, spR); + } + + canvas3d.shaderProgram = spR; + useShaders = true; + } + } + else if (canvas3d.shaderProgram != null) { + canvas3d.shaderProgram.updateNative(canvas3d, false); + canvas3d.shaderProgram = null; + useShaders = false; + } + + // Set the number of available texture units; this depends on + // whether or not shaders are being used. + int availableTextureUnits = + useShaders ? canvas3d.maxTextureImageUnits : canvas3d.maxTextureUnits; + + int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit(); + + // Get the number of active texture units. + // Note that this total number now includes disabled units. + if (app.texUnitState != null) { + TextureUnitStateRetained tus; + + for (int i = 0; i < app.texUnitState.length; i++) { + tus = app.texUnitState[i]; + if (tus != null && tus.isTextureEnabled()) { + lastActiveTexUnitIndex = i; + numActiveTexUnit = i + 1; + if (tus.texAttrs != null) { + useAlpha = useAlpha || + (tus.texAttrs.textureMode == + TextureAttributes.BLEND); + } + } + } + + if (numActiveTexUnit <= availableTextureUnits) { + // Normal, single-pass rendering case + + // update all active texture unit states + for (int i = 0; i < app.texUnitState.length; i++) { + if (i >= availableTextureUnits) { + // This can happen if there are disabled units at + // the end of the array + break; + } + + if ((app.texUnitState[i] != null) && + app.texUnitState[i].isTextureEnabled()) { + app.texUnitState[i].updateNative(i, canvas3d, + false, false); + } else { + canvas3d.resetTexture(canvas3d.ctx, i); + } + } + + // reset the remaining texture units + for (int i = app.texUnitState.length; i < prevNumActiveTexUnit; i++) { + canvas3d.resetTexture(canvas3d.ctx, i); + } + + // set the number active texture unit in Canvas3D + canvas3d.setNumActiveTexUnit(numActiveTexUnit); + + } else { + // Exceeded limit, disable all the texture units + for (int i = 0; i < prevNumActiveTexUnit; i++) { + canvas3d.resetTexture(canvas3d.ctx, i); + } + canvas3d.setNumActiveTexUnit(0); + } + + // set the active texture unit back to 0 + canvas3d.activeTextureUnit(canvas3d.ctx, 0); + } 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) { + app.texture.updateNative(canvas3d); + 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; + + if (!useAlpha) + useAlpha = TransparencyAttributesRetained.useAlpha(app.transparencyAttributes); + + } 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, + 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); + + if(canvas3d.shaderProgram != null) { + canvas3d.shaderProgram.updateNative(canvas3d, false); + canvas3d.shaderProgram = null; + } + + // 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() { + //System.err.println("GC3D.resetAppearance ...."); + + if (canvas3d.material != null) { + canvas3d.updateMaterial(canvas3d.ctx, + red, green, blue, alpha); + canvas3d.material = null; + canvas3d.canvasDirty |= Canvas3D.MATERIAL_DIRTY; + } + + if(canvas3d.shaderProgram != null) { + canvas3d.shaderProgram.updateNative(canvas3d, false); + canvas3d.shaderProgram = null; + // ShaderBin doesn't use dirty bit. + } + + // 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 = new J3dMessage(); + 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 = new J3dMessage(); + 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/javax/media/j3d/Group.java b/src/javax/media/j3d/Group.java new file mode 100644 index 0000000..11dee57 --- /dev/null +++ b/src/javax/media/j3d/Group.java @@ -0,0 +1,555 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_CHILDREN_READ, + ALLOW_COLLISION_BOUNDS_READ + }; + + + /** + * Creates the retained mode GroupRetained object that this + * Group component object will point to. + */ + @Override + 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 index must + * be a value + * greater than or equal to 0 and less than numChildren(). + * @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 child has already + * been added as a child of another group node + * @exception IndexOutOfBoundsException if index 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 index + * must be a value + * greater than or equal to 0 and less than or equal to + * numChildren(). + * @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 child has already + * been added as a child of another group node. + * @exception IndexOutOfBoundsException if index 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 index + * must be a value + * greater than or equal to 0 and less than numChildren(). + * @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 index 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 index + * must be a value + * greater than or equal to 0 and less than numChildren(). + * @exception CapabilityNotSetException if the appropriate capability is + * not set and this group node is part of live or compiled scene graph + * @exception IndexOutOfBoundsException if index is invalid. + */ + public Node getChild(int index) { + if (isLiveOrCompiled() && !this.getCapability(ALLOW_CHILDREN_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("Group9")); + + return ((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() && !this.getCapability(ALLOW_CHILDREN_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("Group9")); + + return ((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 child 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 cloneNode and + * then cloneTree 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate paramter. + * + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree + * flag to be ignored. When false, the value of each + * node's + * duplicateOnCloneTree determines whether data is + * duplicated or copied. + * + * + * @return a reference to the cloned scene graph. + * + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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: + *

    + * collision bounds : null
    + * alternate collision target : false
    + *
+ */ + public Group() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } +} diff --git a/src/javax/media/j3d/GroupRetained.java b/src/javax/media/j3d/GroupRetained.java new file mode 100644 index 0000000..84a6bc6 --- /dev/null +++ b/src/javax/media/j3d/GroupRetained.java @@ -0,0 +1,3207 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +/** + * 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; + // issue 544 + if (VirtualUniverse.mc.useBoxForGroupBounds) { + localBounds = new BoundingBox((Bounds) null); + } else { + localBounds = new BoundingSphere((Bounds)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()) { + J3dMessage message = new J3dMessage(); + 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(); + } + } + } + dirtyBoundsCache(); + } + + // The method that does the work once the lock is acquired. + void doSetChild(Node child, int index) { + 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 + + NodeRetained oldchildr = 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] = new J3dMessage(); + } + } + + if(oldchildr != null) { + oldchildr.setParent(null); + checkClearLive(oldchildr, messages, 0, index, null); + if (this.source.isLive()) { + universe.notifyStructureChangeListeners(false, this.source, (BranchGroup)oldchildr.source); + } + } + removeChildrenData(index); + + if(child == null) { + children.set(index, null); + if (messages != null) { + VirtualUniverse.mc.processMessage(messages); + } + return; + } + + if (this.source.isLive()) { + universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child); + } + 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) { + universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child); + doInsertChild(child, index); + universe.setLiveState.clear(); + } + universe.waitForMC(); + } else { + doInsertChild(child, index); + if (universe != null) { + synchronized (universe.sceneGraphLock) { + universe.setLiveState.clear(); + } + } + } + dirtyBoundsCache(); + } + + // The method that does the work once the lock is acquired. + void doInsertChild(Node child, int index) { + int i; + + insertChildrenData(index); + for (i=index; 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 = children.get(index); + + int size = children.size(); + for (int i = index; i < size; i++) { + NodeRetained child = 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) { + NodeRetained sgo = 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()); + + for (int i = 0; i < children.size(); i++) { + NodeRetained sgo = children.get(i); + if (sgo != null) + userChildren.add((Node)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) { + universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child); + doAddChild(child, null, 0); + universe.setLiveState.clear(); + } + universe.waitForMC(); + } else { + doAddChild(child, null, 0); + if (universe != null) { + synchronized (universe.sceneGraphLock) { + universe.setLiveState.clear(); + } + } + } + dirtyBoundsCache(); + } + + // 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 (bg != null) { + ((GroupRetained)bg.retained).dirtyBoundsCache(); + } + if (this.source.isLive()) { + universe.resetWaitMCFlag(); + synchronized (universe.sceneGraphLock) { + GroupRetained oldParent = (GroupRetained)((BranchGroupRetained)bg.retained).parent; + doMoveTo(bg); + universe.setLiveState.clear(); + if (oldParent==null) + universe.notifyStructureChangeListeners(((BranchGroupRetained)bg.retained).locale, this.source, bg); + else + universe.notifyStructureChangeListeners(oldParent.source, this.source, bg); + } + universe.waitForMC(); + } else { + doMoveTo(bg); + if (universe != null) { + synchronized (universe.sceneGraphLock) { + universe.setLiveState.clear(); + } + } + } + dirtyBoundsCache(); + } + + // 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 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]; + } + } + 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) { + int index; + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = lights.get(hkIndex); + if (l != null) { + for (int i = 0; i < numLgt; i++) { + index = l.indexOf(removelight[i]); + l.remove(index); + } + } + } + else { + ArrayList l = lights.get(0); + for (int i = 0; i < numLgt; i++) { + index = l.indexOf(removelight[i]); + l.remove(index); + } + } + + /* + // XXXX: 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = lights.get(i); + if (l != null) { + newLights.add(new ArrayList(l)); + } + 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 = fogs.get(i); + if (l != null) { + newFogs.add(new ArrayList(l)); + } + 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 = modelClips.get(i); + if (l != null) { + newMclips.add(new ArrayList(l)); + } + 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 = altAppearances.get(i); + if (l != null) { + newAltApps.add(new ArrayList(l)); + } + 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) { + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = lights.get(hkIndex); + if (l != null) { + for (int i = 0; i < numLgts; i++) { + l.add(addlight[i]); + } + } + } + else { + ArrayList l = 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) { + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = fogs.get(hkIndex); + if (l != null) { + l.add(fog); + } + } + else { + ArrayList l = fogs.get(0); + l.add(fog); + } + + } + + // Add a ModelClip to the list of ModelClip + void addModelClip(ModelClipRetained modelClip, HashKey key) { + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = modelClips.get(hkIndex); + if (l != null) { + l.add(modelClip); + } + } + else { + ArrayList l = modelClips.get(0); + l.add(modelClip); + } + + } + // Add a alt appearance to the list of alt appearance + void addAltApp(AlternateAppearanceRetained altApp, HashKey key) { + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = altAppearances.get(hkIndex); + if (l != null) { + l.add(altApp); + } + } + else { + ArrayList l = altAppearances.get(0); + l.add(altApp); + } + + } + + + // Remove a fog from the list of fogs + void removeFog(FogRetained fog, HashKey key) { + int index; + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = fogs.get(hkIndex); + if (l != null) { + index = l.indexOf(fog); + l.remove(index); + } + } + else { + ArrayList l = fogs.get(0); + index = l.indexOf(fog); + l.remove(index); + } + + } + + + // Remove a ModelClip from the list of ModelClip + void removeModelClip(ModelClipRetained modelClip, HashKey key) { + int index; + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = modelClips.get(hkIndex); + if (l != null) { + index = l.indexOf(modelClip); + l.remove(index); + } + } + else { + ArrayList l = 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) { + int index; + if (inSharedGroup) { + int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + ArrayList l = altAppearances.get(hkIndex); + if (l != null) { + index = l.indexOf(altApp); + l.remove(index); + } + } + else { + ArrayList l = altAppearances.get(0); + index = l.indexOf(altApp); + l.remove(index); + } + + } + + + @Override + void updatePickable(HashKey keys[], boolean pick[]) { + int numChildLessOne = children.size() - 1; + super.updatePickable(keys, pick); + int i=0; + + // Fix for issue 540 + if (numChildLessOne < 0) { + return; + } + // End fix for issue 540 + + for (i = 0; i < numChildLessOne; i++) { + NodeRetained child = children.get(i); + if(child != null) + child.updatePickable(keys, (boolean []) pick.clone()); + } + // No need to clone for the last value + + NodeRetained child = children.get(i); + if(child != null) + child.updatePickable(keys, pick); + + } + + + @Override + void updateCollidable(HashKey keys[], boolean collide[]) { + int numChildLessOne = children.size() - 1; + super.updateCollidable(keys, collide); + int i=0; + + // Fix for issue 540 + if (numChildLessOne < 0) { + return; + } + // End fix for issue 540 + + for (i = 0; i < numChildLessOne; i++) { + NodeRetained child = children.get(i); + if (child != null) + child.updateCollidable(keys, (boolean[]) collide.clone()); + } + // No need to clone for the last value + NodeRetained child = 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 = new J3dMessage(); + 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(branchGroupPaths); + 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] = new J3dMessage(); + } + 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 = 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; + + // Issue 312: Allocate data structures if we are in a ViewSpecificGroup + 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>(); + } + + if (this instanceof OrderedGroupRetained && linkNode == null) { + // set this regardless of refCount + OrderedGroupRetained og = (OrderedGroupRetained)this; + s.ogList.add(og); + s.ogChildIdList.add(new Integer(childIndex)); + s.ogCIOList.add(og); + int[] newArr = null; + if(og.userChildIndexOrder != null) { + newArr = new int[og.userChildIndexOrder.length]; + System.arraycopy(og.userChildIndexOrder, 0, newArr, + 0, og.userChildIndexOrder.length); + } + s.ogCIOTableList.add(newArr); + + } + + // Issue 312: always initialize s.viewLists + 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] = new J3dMessage(); + } + 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(); + } + + // Issue 312: We need to send the REMOVE_NODES message to the + // RenderingEnvironmentStructure before we send VIEWSPECIFICGROUP_CLEAR, + // since the latter clears the list of views that is referred to by + // scopedNodesViewList and used by removeNodes. + 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(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; + } + + 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= 0) { + newTargets[i] = new Targets(); + } else { + newTargets[i] = null; + } + } + s.transformTargets = newTargets; + + // XXXX: 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= 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 = childrenSwitchLinks.get(i); + s.parentSwitchLink = this; + + } else { + if (nodeType == NodeRetained.SHAREDGROUP) { + i = parentSwitchLinkChildIndex; + s.childSwitchLinks = childrenSwitchLinks.get(i); + s.parentSwitchLink = this; + + } else { + s.parentSwitchLink = parentSwitchLink; + if (parentSwitchLink != null) { + i = parentSwitchLinkChildIndex; + s.childSwitchLinks = 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=0; i--) { + NodeRetained child = children.get(i); + if(child != null) + child.updateLocalToVworld(); + } + } + + @Override + void setNodeData(SetLiveState s) { + super.setNodeData(s); + orderedPaths = s.orderedPaths; + } + + @Override + 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); + } + + + + @Override + 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. + */ + @Override + void doSetLive(SetLiveState s) { + int i, nchildren; + 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(oldBranchGroupPaths); + 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 = lights.get(hkIndex); + ArrayList src = 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 = lights.get(0); + ArrayList src = 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 = fogs.get(hkIndex); + ArrayList src = 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 = fogs.get(0); + ArrayList src = 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 = modelClips.get(hkIndex); + ArrayList src = 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 = modelClips.get(0); + ArrayList src = 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 = altAppearances.get(hkIndex); + ArrayList src = 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 = altAppearances.get(0); + ArrayList src = 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.err.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.err.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); + } + } + + @Override + void computeCombineBounds(Bounds bounds) { + if (!VirtualUniverse.mc.cacheAutoComputedBounds) { + if (boundsAutoCompute) { + for (int i=children.size()-1; i>=0; i--) { + NodeRetained child = children.get(i); + if(child != null) + child.computeCombineBounds(bounds); + } + } else { + // Should this be lock too ? ( MT safe ? ) + synchronized(localBounds) { + bounds.combine(localBounds); + } + } + } else { + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + if (validCachedBounds && boundsAutoCompute) { + bounds.combine(cachedBounds); + return; + } + + if (boundsAutoCompute) { + // issue 544 + if (VirtualUniverse.mc.useBoxForGroupBounds) { + cachedBounds = new BoundingBox((Bounds) null); + } else { + cachedBounds = new BoundingSphere((Bounds)null); + } + for (int i = children.size() - 1; i >= 0; i--) { + NodeRetained child = children.get(i); + if (child != null) { + child.computeCombineBounds(cachedBounds); + } + } + bounds.combine(cachedBounds); + } 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 + */ + @Override + Bounds getBounds() { + + if ( boundsAutoCompute) { + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + if (validCachedBounds) { + return (Bounds) cachedBounds.clone(); + } + // issue 544 + Bounds boundingObject = null; + if (VirtualUniverse.mc.useBoxForGroupBounds) { + boundingObject = new BoundingBox((Bounds) null); + } else { + boundingObject = new BoundingSphere((Bounds)null); + } + for (int i = children.size() - 1; i >= 0; i--) { + NodeRetained child = children.get(i); + if (child != null) { + child.computeCombineBounds(boundingObject); + } + } + + return boundingObject; + } + return super.getBounds(); + } + + /** + * Gets the bounding object of a node. + * @return the node's bounding object + */ + @Override + Bounds getEffectiveBounds() { + if ( boundsAutoCompute) { + return getBounds(); + } + return super.getEffectiveBounds(); + } + + // returns true if children cannot be read/written and none of the + // children can read their parent (i.e., "this") group node + boolean isStaticChildren() { + if (source.getCapability(Group.ALLOW_CHILDREN_READ) || + source.getCapability(Group.ALLOW_CHILDREN_WRITE)) { + return false; + } + + for (int i = children.size() - 1; i >= 0; i--) { + NodeRetained nodeR = children.get(i); + if (nodeR != null && nodeR.source.getCapability(Node.ALLOW_PARENT_READ)) { + return false; + } + } + + return true; + } + + + @Override + boolean isStatic() { + return (super.isStatic() && isStaticChildren()); + } + + /** + * This compiles() a group + */ + @Override + void setCompiled() { + super.setCompiled(); + for (int i=children.size()-1; i>=0; i--) { + NodeRetained node = children.get(i); + if (node != null) + node.setCompiled(); + } + } + + @Override + void traverse(boolean sameLevel, int level) { + if (!sameLevel) { + super.traverse(true, level); + + if (source.getCapability(Group.ALLOW_CHILDREN_READ)) { + System.err.print(" (r)"); + } else if (isStatic()) { + System.err.print(" (s)"); + } else if (source.getCapability(Group.ALLOW_CHILDREN_WRITE)) { + System.err.print(" (w)"); + } + } + + level++; + for (int i = 0; i < children.size(); i++) { + NodeRetained node = children.get(i); + if (node != null) { + node.traverse(false, level); + } + } + } + + @Override + void compile(CompileState compState) { + 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++) { + NodeRetained node = children.get(i); + if (node != null) { + node.compile(compState); + } + } + + if (J3dDebug.devPhase && J3dDebug.debug) { + compState.numGroups++; + } + } + + @Override + void merge(CompileState compState) { + + GroupRetained saveParentGroup = null; + + 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++) { + NodeRetained node = 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. + */ + @Override + void clearLive(SetLiveState s) { + int i, k, hkIndex, nchildren; + 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--) { + NodeRetained child = 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(og); + s.ogChildIdList.add(new Integer(i)); + } + s.orderedPaths = og.childrenOrderedPaths.get(i); + } + + if (child != null) { + child.clearLive(s); + } + } + // Has its own copy + // XXXX: 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 = savedParentLights.get(hkIndex); + ArrayList gl = lights.get(hkIndex); + if (l != null) { + size = l.size(); + for (k = 0; k < size; k++) { + gl.remove(l.get(k)); + } + } + + } + } + else { + ArrayList l = savedParentLights.get(0); + ArrayList gl = 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 = savedParentFogs.get(hkIndex); + ArrayList gl = fogs.get(hkIndex); + if (l != null) { + size = l.size(); + for (k = 0; k < size; k++) { + gl.remove(l.get(k)); + } + } + + } + } + else { + ArrayList l = 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 = savedParentMclips.get(hkIndex); + ArrayList gl = modelClips.get(hkIndex); + if (l != null) { + size = l.size(); + for (k = 0; k < size; k++) { + gl.remove(l.get(k)); + } + } + + } + } + else { + ArrayList l = 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 = savedParentAltApps.get(hkIndex); + ArrayList gl = altAppearances.get(hkIndex); + if (l != null) { + size = l.size(); + for (k = 0; k < size; k++) { + gl.remove(l.get(k)); + } + } + + } + } + else { + ArrayList l = 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 = 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 = 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 + @Override + public BoundingBox computeBoundingHull() { + return collisionVwcBounds; + } + + // If isSwitchOn cached here, we don't need to traverse up the tree + @Override + 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. + @Override + public boolean isEnable(int vis) { + return isNodeSwitchOn(this.sourceNode, key); + } + + // Can't use getLocale, it is used by BranchGroupRetained + @Override + 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 = 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); + } + } + + @Override + void setBoundsAutoCompute(boolean autoCompute) { + if (autoCompute != boundsAutoCompute) { + super.setBoundsAutoCompute(autoCompute); + if (!autoCompute) { + localBounds = getEffectiveBounds(); + } + if (source.isLive() && collisionBound == null && autoCompute + && mirrorGroup != null) { + + J3dMessage message = new J3dMessage(); + 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); + } + } + } + + @Override + void setBounds(Bounds bounds) { + super.setBounds(bounds); + if (source.isLive() && !boundsAutoCompute && + collisionBound == null && mirrorGroup != null) { + + J3dMessage message = new J3dMessage(); + 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); + } + } + + + @Override + 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 = 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 parents = ((SharedGroupRetained)parent).parents; + + if (parentSwitchLinkChildIndex == -1) { + parentSwitchLinkChildIndex = + ((GroupRetained)parent).children.indexOf(child); + } + + for(j=0; j< parents.size(); j++) { + NodeRetained ln = 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=0; i--) { + NodeRetained child = children.get(i); + child.searchGeometryAtoms(list); + } + } +} diff --git a/src/javax/media/j3d/HashKey.java b/src/javax/media/j3d/HashKey.java new file mode 100644 index 0000000..cfbf7fe --- /dev/null +++ b/src/javax/media/j3d/HashKey.java @@ -0,0 +1,257 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 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; + } + + @Override + 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; + } + + @Override + 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; + } + + @Override + 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 + * 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. + *

+ * 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 + */ + @Override + 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/javax/media/j3d/IllegalRenderingStateException.java b/src/javax/media/j3d/IllegalRenderingStateException.java new file mode 100644 index 0000000..023ad9a --- /dev/null +++ b/src/javax/media/j3d/IllegalRenderingStateException.java @@ -0,0 +1,49 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Indicates an illegal state for rendering. This is typically some sort of + * resource or graphics device error encountered during rendering. + */ +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/javax/media/j3d/IllegalSceneGraphException.java b/src/javax/media/j3d/IllegalSceneGraphException.java new file mode 100644 index 0000000..64542aa --- /dev/null +++ b/src/javax/media/j3d/IllegalSceneGraphException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Indicates an illegal Java 3D scene graph. + * For example, the following is illegal: + *

    + *
  • A ViewPlatform node under a ViewSpecificGroup
  • + *
+ * + * @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/javax/media/j3d/IllegalSharingException.java b/src/javax/media/j3d/IllegalSharingException.java new file mode 100644 index 0000000..e93d5f8 --- /dev/null +++ b/src/javax/media/j3d/IllegalSharingException.java @@ -0,0 +1,74 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Indicates an illegal attempt to share a scene graph object. For example, + * the following are illegal: + *
    + *
  • referencing a shared subgraph in more than one virtual universe
  • + *
  • using the same node both in the scene graph and in an + * immediate mode graphics context
  • + *
  • including any of the following unsupported types of leaf node within a shared subgraph:
  • + *
      + *
    • AlternateAppearance
    • + *
    • Background
    • + *
    • Behavior
    • + *
    • BoundingLeaf
    • + *
    • Clip
    • + *
    • Fog
    • + *
    • ModelClip
    • + *
    • Soundscape
    • + *
    • ViewPlatform
    • + *
    + *
  • referencing a BranchGroup node in more than one of the following + * ways:
  • + *
      + *
    • attaching it to a (single) Locale
    • + *
    • adding it as a child of a Group Node within the scene graph
    • + *
    • referencing it from a (single) Background Leaf Node as + * background geometry
    • + *
    + *
+ */ +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/javax/media/j3d/ImageComponent.java b/src/javax/media/j3d/ImageComponent.java new file mode 100644 index 0000000..85d7aa4 --- /dev/null +++ b/src/javax/media/j3d/ImageComponent.java @@ -0,0 +1,434 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * 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. + * + *

+ *

    + *
  • + * By Copying: + * 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. + *
  • + *
  • By Reference: + * 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 byReference flag set to true. + * 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 yUp 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. + *
  • + *
+ * + *

+ * 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 yUp flag to true in conjunction + * with setting the byReference flag to true makes it + * possible for Java 3D to avoid copying the texture map in some + * cases. + * + *

+ * 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: + *

    byteVariable = (byte) intValue; // intValue can be > 127
+ * 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; + + + /** + * Used to specify the class of the image being wrapped. + * + * @since Java 3D 1.5 + */ + public enum ImageClass { + /** + * Indicates that this ImageComponent object wraps a BufferedImage + * object. This is the default state. Note that the image class will + * be BUFFERED_IMAGE following a call to set(RenderedImage image) + * if we are in by-copy mode, or if the image is an instance of + * BufferedImage. + */ + BUFFERED_IMAGE, + + /** + * Indicates that this ImageComponent object wraps a RenderedImage + * object that is not a BufferedImage. Note that the image class + * of an ImageComponent following a call to set(RenderedImage image) + * will be RENDERED_IMAGE, if and only if the image is not an instance + * of BufferedImage and the ImageComponent is in by-reference mode. + */ + RENDERED_IMAGE, + + /** + * Indicates that this ImageComponent object wraps an NioImageBuffer + * object. Note that an ImageComponent in this state must not be used + * as the off-screen buffer of a Canvas3D nor as the target of a + * readRaster operation. + */ + NIO_IMAGE_BUFFER, + } + + + /** + * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SIZE_READ, + ALLOW_IMAGE_READ, + ALLOW_FORMAT_READ + }; + + /** + * Not a public constructor, for internal use + */ + ImageComponent() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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: + *
    + * byReference : false
    + * yUp : false
    + *
+ * + * @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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 true if the data access mode for this + * ImageComponent object is by-reference; + * false 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. + * + * @exception IllegalStateException if the image class of this object + * is ImageClass.NIO_IMAGE_BUFFER. + * + * @deprecated as of Java 3D 1.5, the yUp flag should only be set at object + * construction time. + * + * @since Java 3D 1.2 + */ + public void setYUp(boolean yUp) { + checkForLiveOrCompiled(); + + // check for illegal image class + if (((ImageComponentRetained)this.retained).getImageClass() == ImageClass.NIO_IMAGE_BUFFER) { + throw new IllegalStateException("ImageComponent4"); + } + + ((ImageComponentRetained)this.retained).setYUp(yUp); + } + + + /** + * Retrieves the y-orientation for this ImageComponent object. + * + * @return true if the y-orientation of this + * ImageComponent object is y-up; false if the + * y-orientation of this ImageComponent object is y-down. + * + * @since Java 3D 1.2 + */ + public boolean isYUp() { + return ((ImageComponentRetained)this.retained).isYUp(); + } + + + /** + * Retrieves the image class of this ImageComponent object. + * + * @return the image class of this ImageComponent, + * one of: ImageClass.BUFFERED_IMAGE, + * ImageClass.RENDERED_IMAGE, or ImageClass.NIO_IMAGE_BUFFER. + * + * @since Java 3D 1.5 + */ + public ImageClass getImageClass() { + return ((ImageComponentRetained)this.retained).getImageClass(); + } + +} diff --git a/src/javax/media/j3d/ImageComponent2D.java b/src/javax/media/j3d/ImageComponent2D.java new file mode 100644 index 0000000..d8e0d2f --- /dev/null +++ b/src/javax/media/j3d/ImageComponent2D.java @@ -0,0 +1,743 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.util.logging.Level; + +/** + * 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: + * + *

+ *

    + * + * BufferedImage bi;
    + * RenderedImage ri = bi;
    + * ImageComponent2D ic;
    + *

    + * // Set the image to the specified BufferedImage
    + * ic.set(bi);
    + *

    + * // Set the image to the specified RenderedImage
    + * ic.set(ri);
    + *
    + *

+ * + *

+ * As of Java 3D 1.5, an ImageComponent2D accepts an NioImageBuffer object + * as an alternative to a RenderedImage. + */ + +public class ImageComponent2D extends ImageComponent { + + // non-public, no parameter constructor + ImageComponent2D() {} + + /** + * Constructs a 2D image component object using the specified + * format, width, and height. Default values are used for + * all other parameters. The default values are as follows: + *

    + * image : null
    + * imageClass : ImageClass.BUFFERED_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) { + + if (MasterControl.isDevLoggable(Level.FINER)) { + MasterControl.getDevLogger().finer("ImageComponent - using default of byCopy"); + } + ((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. + * The image class is set to ImageClass.BUFFERED_IMAGE. + * Default values are used for all other parameters. + * + * @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) { + + if (MasterControl.isDevLoggable(Level.FINER)) { + MasterControl.getDevLogger().finer("ImageComponent - using default of byCopy"); + } + ((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 + * and RenderedImage. A copy of the RenderedImage is made. + * The image class is set to ImageClass.BUFFERED_IMAGE. + * Default values are used for all other parameters. + * + * @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) { + + + if (MasterControl.isDevLoggable(Level.FINER)) { + MasterControl.getDevLogger().finer("ImageComponent - using default of byCopy"); + } + ((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. + * 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 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) { + + if (MasterControl.isDevLoggable(Level.INFO)) { + if (byReference && !yUp) { + MasterControl.getDevLogger().info("ImageComponent - yUp should " + + "be set when using byReference, " + + "otherwise an extra copy of the image will be created"); + } + } + + ((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. + * The image class is set to ImageClass.BUFFERED_IMAGE. + * + * @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) { + + if (MasterControl.isDevLoggable(Level.INFO)) { + if (byReference && !yUp) { + MasterControl.getDevLogger().info("ImageComponent - yUp should " + + "be set when using byReference, " + + "otherwise an extra copy of the image will be created"); + } + } + + ((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); + } + + /** + * Constructs a 2D image component object using the specified format, + * RenderedImage, byReference flag, and yUp flag. + * The image class is set to ImageClass.RENDERED_IMAGE if the byReferece + * flag is true and the specified RenderedImage is not an instance + * of BufferedImage. In all other cases, the image class is set to + * ImageClass.BUFFERED_IMAGE. + * + * @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) { + + if (MasterControl.isDevLoggable(Level.INFO)) { + if (byReference && !yUp) + MasterControl.getDevLogger().info("ImageComponent - yUp should " + + "be set when using byReference, " + + "otherwise an extra copy of the image will be created"); + } + ((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); + } + + /** + * Constructs a 2D image component object using the specified format, + * NioImageBuffer, byReference flag, and yUp flag. + * The image class is set to ImageClass.NIO_IMAGE_BUFFER. + * + * @param format the image component format, one of: FORMAT_RGB, + * FORMAT_RGBA, etc. + * @param image the NioImageBuffer 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. + * + * @exception IllegalArgumentException if the byReference flag is false. + * + * @exception IllegalArgumentException if the yUp flag is false. + * + * @exception IllegalArgumentException if the number of components in format + * does not match the number of components in image. + * + * @since Java 3D 1.5 + */ + public ImageComponent2D(int format, + NioImageBuffer 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. + *

+ * The image class is set to ImageClass.BUFFERED_IMAGE. + * + * @param image BufferedImage object containing the image. + * Its size must be the same as the current size of this + * ImageComponent2D object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalArgumentException if the width and height of the + * specified image is not equal to the width and height of this + * ImageComponent object. + */ + 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. + *

+ * The image class is set to ImageClass.RENDERED_IMAGE if the the + * data access mode is by-reference and the specified + * RenderedImage is not an instance of BufferedImage. In all + * other cases, the image class is set to ImageClass.BUFFERED_IMAGE. + * + * @param image RenderedImage object containing the image. + * Its size must be the same as the current size of this + * ImageComponent2D object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalArgumentException if the width and height of the + * specified image is not equal to the width and height of this + * ImageComponent object. + * + * @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); + } + + /** + * Sets this image component to the specified NioImageBuffer + * object. If the data access mode is not by-reference, the + * NioImageBuffer data is copied into this object. If + * the data access mode is by-reference, a reference to the + * NioImageBuffer is saved, but the data is not necessarily + * copied. + *

+ * The image class is set to ImageClass.NIO_IMAGE_BUFFER. + * + * @param image NioImageBuffer object containing the image. + * Its size must be the same as the current size of this + * ImageComponent2D object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalStateException if this ImageComponent object + * is not yUp. + * + * @exception IllegalArgumentException if the width and height of the + * specified image is not equal to the width and height of this + * ImageComponent object. + * + * @exception IllegalArgumentException if the number of components in format + * does not match the number of components in image. + * + * @since Java 3D 1.5 + */ + public void set(NioImageBuffer 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 image class is not + * ImageClass.BUFFERED_IMAGE. + */ + 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 + * + * @exception IllegalStateException if the image class is not one of: + * ImageClass.BUFFERED_IMAGE or ImageClass.RENDERED_IMAGE. + * + * @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(); + } + + /** + * 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 NioImageBuffer object created from the data + * in this image component, or the NioImageBuffer 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 image class is not + * ImageClass.NIO_IMAGE_BUFFER. + * + * @since Java 3D 1.5 + */ + public NioImageBuffer getNioImage() { + + if (isLiveOrCompiled()) { + if (!this.getCapability(ImageComponent.ALLOW_IMAGE_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("ImageComponent2D0")); + } + } + return ((ImageComponent2DRetained)this.retained).getNioImage(); + + } + + + /** + * 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 specified RenderedImage object must be of the same format as + * the current RenderedImage object in this image component. + * 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 + * BY_REFERENCE. + * + * @exception IllegalArgumentException if width or + * height of + * the subregion exceeds the dimension of the image of this object. + * + * @exception IllegalArgumentException if dstX < 0, or + * (dstX + width) > width of this object, or + * dstY < 0, or + * (dstY + height) > height of this object. + * + * @exception IllegalArgumentException if srcX < 0, or + * (srcX + width) > width of the RenderedImage + * object containing the subimage, or + * srcY < 0, or + * (srcY + height) > height of the + * RenderedImage object containing the subimage. + * + * @exception IllegalArgumentException if the specified RenderedImage + * is not compatible with the existing RenderedImage. + * + * @exception IllegalStateException if the image class is not one of: + * ImageClass.BUFFERED_IMAGE or ImageClass.RENDERED_IMAGE. + * + * @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(); + + // Fix to issue 492 + if ((srcX < 0) || (srcY < 0) || + ((srcX + width) > image.getWidth()) || ((srcY + height) > image.getHeight()) || + (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. + *

+ * 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. + *

+ * @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 + * BY_COPY. + * @exception IllegalArgumentException if width or + * height of + * the subregion exceeds the dimension of the image of this object. + * @exception IllegalArgumentException if x < 0, or + * (x + width) > width of this object, or + * y < 0, or + * (y + height) > 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. + */ + @Override + void createRetained() { + this.retained = new ImageComponent2DRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + ImageComponent2DRetained rt = (ImageComponent2DRetained) retained; + + ImageComponent2D img = new ImageComponent2D(rt.getFormat(), + rt.width, + rt.height, + rt.byReference, + rt.yUp); + img.duplicateNodeComponent(this); + return img; + } + + + /** + * Copies all node information from originalNodeComponent + * into the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + void duplicateAttributes(NodeComponent originalNodeComponent, + boolean forceDuplicate) { + super.duplicateAttributes(originalNodeComponent, forceDuplicate); + + ImageComponent.ImageClass imageClass = + ((ImageComponentRetained)originalNodeComponent.retained).getImageClass(); + if(imageClass == ImageComponent.ImageClass.NIO_IMAGE_BUFFER) { + NioImageBuffer nioImg = ((ImageComponent2DRetained) + originalNodeComponent.retained).getNioImage(); + + if(nioImg != null) { + ((ImageComponent2DRetained) retained).set(nioImg); + } + } else { + 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 updateData 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. + *
+ * NOTE: Applications should not 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/javax/media/j3d/ImageComponent2DRetained.java b/src/javax/media/j3d/ImageComponent2DRetained.java new file mode 100644 index 0000000..395b159 --- /dev/null +++ b/src/javax/media/j3d/ImageComponent2DRetained.java @@ -0,0 +1,337 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + +class ImageComponent2DRetained extends ImageComponentRetained { + + ImageComponent2DRetained() { + } + + /** + * This method handles NioImageBuffer + * Refers or copies the specified NioImageBuffer to this 2D image component object. + * @param image NioImageBuffer object containing the image. + * The format and size must be the same as the current format in this + * ImageComponent2D object. + */ + void set(NioImageBuffer image) { + + int width = image.getWidth(); + int height = image.getHeight(); + + if (!byReference) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2D7")); + } + if (!yUp) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2D8")); + } + + if (width != this.width) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained0")); + } + if (height != this.height) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent2DRetained1")); + } + + geomLock.getLock(); + + setImageClass(image); + + // This is a byRef image. + setRefImage(image,0); + + // Reset this flag to true, incase it was set to false due to + // the previous image type. + abgrSupported = true; + + imageTypeIsSupported = isImageTypeSupported(image); + + if (imageTypeIsSupported) { + + /* Use reference when ( format is OK, Yup is true, and byRef is true). */ + // Create image data object with the byRef image. */ + imageData = createNioImageBufferDataObject(image); + + } else { + // Handle abgrSupported is false case. + imageData = createNioImageBufferDataObject(null); + copyUnsupportedNioImageToImageData(image, 0, 0, 0, 0, width, height, imageData); + + } + + geomLock.unLock(); + + if (source.isLive()) { + // send a IMAGE_CHANGED message in order to + // notify all the users of the change + sendMessage(IMAGE_CHANGED, null); + } + } + + /** + * This method handles both BufferedImage and RenderedImage + * Copies the specified RenderedImage to this 2D image component object. + * @param image RenderedImage object containing the image. + * The format and size must be the same as the current format in this + * ImageComponent2D object. + */ + void set(RenderedImage image) { + + 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")); + } + + setImageClass(image); + + geomLock.getLock(); + + if (byReference) { + setRefImage(image,0); + } + + // Reset this flag to true, incase it was set to false due to + // the previous image type. + abgrSupported = true; + + imageTypeIsSupported = isImageTypeSupported(image); + + if (imageTypeIsSupported) { + + if (byReference && yUp) { + /* Use reference when ( format is OK, Yup is true, and byRef is true). */ + // System.err.println("ImageComponent2DRetained.set() : (imageTypeSupported && byReference && yUp) --- (1)"); + if (image instanceof BufferedImage) { + // Create image data object with the byRef image. */ + imageData = createRenderedImageDataObject(image); + } + else { + // System.err.println("byRef and not BufferedImage !!!"); + imageData = null; + } + + } else { + // Either not byRef or not yUp or not both + // System.err.println("ImageComponent2DRetained.set() : (imageTypeSupported && ((!byReference && yUp) || (imageTypeSupported && !yUp)) --- (2)"); + + // Create image data object with buffer for image. */ + imageData = createRenderedImageDataObject(null); + copySupportedImageToImageData(image, 0, imageData); + } + + } else { + // image type is unsupported, need to create a supported local copy. + // TODO : borrow code from JAI to convert to right format. + // System.err.println("ImageComponent2DRetained.set() : (imageTypeSupported == false) --- (4)"); + /* Will use the code segment in copy() method */ + + // Create image data object with buffer for image. */ + imageData = createRenderedImageDataObject(null); + copyUnsupportedImageToImageData(image, 0, imageData); + + } + + geomLock.unLock(); + + if (source.isLive()) { + // send a IMAGE_CHANGED message in order to + // notify all the users of the change + sendMessage(IMAGE_CHANGED, null); + } + } + + void setSubImage(RenderedImage image, int width, int height, + int srcX, int srcY, int dstX, int dstY) { + + if (!isSubImageTypeEqual(image)) { + throw new IllegalStateException( + J3dI18N.getString("ImageComponent2D6")); + } + + // Can't be byReference + assert (!byReference); + assert (imageData != null); + + geomLock.getLock(); + + if (imageTypeIsSupported) { + // Either not byRef or not yUp or not both + // System.err.println("ImageComponent2DRetained.setSubImage() : (imageTypeSupported ) --- (1)"); + if (image instanceof BufferedImage) { + copyImageLineByLine((BufferedImage)image, srcX, srcY, dstX, dstY, 0, width, height, imageData); + } + else { + copySupportedImageToImageData(image, srcX, srcY, dstX, dstY, 0, width, height, imageData); + } + } else { + // image type is unsupported, need to create a supported local copy. + // TODO : Should look into borrow code from JAI to convert to right format. + // System.err.println("ImageComponent2DRetained.setSubImage() : (imageTypeSupported == false) --- (2)"); + if (image instanceof BufferedImage) { + copyUnsupportedImageToImageData((BufferedImage)image, srcX, srcY, dstX, dstY, 0, width, height, imageData); + } + else { + copyUnsupportedImageToImageData(image, srcX, srcY, dstX, dstY, 0, width, height, imageData); + } + } + geomLock.unLock(); + + if (source.isLive()) { + + // send a SUBIMAGE_CHANGED message in order to + // notify all the users of the change + + ImageComponentUpdateInfo info; + + info = new ImageComponentUpdateInfo(); + info.x = dstX; + info.y = dstY; + info.z = 0; + info.width = width; + info.height = height; + + sendMessage(SUBIMAGE_CHANGED, info); + } + } + + /** + * Retrieves a copy of the image in this ImageComponent2D object. + * @return a new RenderedImage object created from the image in this + * ImageComponent2D object + */ + RenderedImage getImage() { + + if (isByReference()) { + return (RenderedImage) getRefImage(0); + } + + if(imageData != null) { + return imageData.createBufferedImage(0); + } + + return null; + } + + /** + * Retrieves the reference of the nio image in this ImageComponent2D object. + */ + NioImageBuffer getNioImage() { + + if (getImageClass() != ImageComponent.ImageClass.NIO_IMAGE_BUFFER) { + throw new IllegalStateException(J3dI18N.getString("ImageComponent2D9")); + } + + assert (byReference == true); + + return (NioImageBuffer) getRefImage(0); + } + + /** + * 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); + + Object refImage = getRefImage(0); + assert (refImage != null); + assert (imageData != null); + + // Check is data copied internally. + if(!imageData.isDataByRef()) { + // update the internal copy of the image data if a copy has been + // made + if (imageTypeIsSupported) { + assert !(refImage instanceof NioImageBuffer); + + if (refImage instanceof BufferedImage) { + copyImageLineByLine((BufferedImage)refImage, x, y, x, y, 0, width, height, imageData); + } else { + RenderedImage ri = (RenderedImage)refImage; + copySupportedImageToImageData(ri, (x + ri.getMinX()), (y + ri.getMinY()), x, y, 0, width, height, imageData); + } + } else { + // image type is unsupported, need to create a supported local copy. + // TODO : Should look into borrow code from JAI to convert to right format. + if (refImage instanceof BufferedImage) { + copyUnsupportedImageToImageData((BufferedImage)refImage, x, y, x, y, 0, width, height, imageData); + } else if (refImage instanceof RenderedImage) { + RenderedImage ri = (RenderedImage)refImage; + copyUnsupportedImageToImageData(ri, (x + ri.getMinX()), (y + ri.getMinY()), x, y, 0, width, height, imageData); + } else if (refImage instanceof NioImageBuffer) { + copyUnsupportedNioImageToImageData((NioImageBuffer)refImage, x, y, x, y, width, height, imageData); + } else { + assert false; + } + } + } + geomLock.unLock(); + + + if (source.isLive()) { + + // send a SUBIMAGE_CHANGED message in order to + // notify all the users of the change + + ImageComponentUpdateInfo info; + + info = new ImageComponentUpdateInfo(); + info.x = x; + info.y = y; + info.z = 0; + info.width = width; + info.height = height; + + sendMessage(SUBIMAGE_CHANGED, info); + } + } + + @Override + void clearLive(int refCount) { + super.clearLive(refCount); + } + +} diff --git a/src/javax/media/j3d/ImageComponent3D.java b/src/javax/media/j3d/ImageComponent3D.java new file mode 100644 index 0000000..34be8b9 --- /dev/null +++ b/src/javax/media/j3d/ImageComponent3D.java @@ -0,0 +1,979 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + * + *

+ *

    + * + * BufferedImage bi;
    + * RenderedImage ri = bi;
    + * ImageComponent3D ic;
    + *

    + * // Set image 0 to the specified BufferedImage
    + * ic.set(0, bi);
    + *

    + * // Set image 0 to the specified RenderedImage
    + * ic.set(0, ri);
    + *
    + *

+ * + */ +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: + *
    + * array of images : null
    + * imageClass : ImageClass.BUFFERED_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 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); + } + + /** + * Constructs a 3D image component object using the specified format, + * and the BufferedImage array. + * The image class is set to ImageClass.BUFFERED_IMAGE. + * 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); + for (int i=0; inot an + * instance of BufferedImage. In all other cases, the image class is set to + * ImageClass.BUFFERED_IMAGE. + * + * @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); + for (int i=0; i + * The image class is set to ImageClass.BUFFERED_IMAGE. + * + * @param images array of BufferedImage objects containing the image. + * The size (width and height) of each image must be the same as the + * size of the image component, and the length of the images array + * must equal the depth of the image component. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalArgumentException if the length of the images array is + * not equal to the depth of this ImageComponent object. + * + * @exception IllegalArgumentException if the width and height of each + * image in the images array is not equal to the width and height of this + * ImageComponent object. + */ + 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 + * The image class is set to ImageClass.RENDERED_IMAGE if the data access + * mode is by-reference and any of the specified RenderedImages + * is not an instance of BufferedImage. In all other cases, + * the image class is set to ImageClass.BUFFERED_IMAGE. + * + * @param images array of RenderedImage objects containing the image. + * The size (width and height) of each image must be the same as the + * size of the image component, and the length of the images array + * must equal the depth of the image component. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalArgumentException if the length of the images array is + * not equal to the depth of this ImageComponent object. + * + * @exception IllegalArgumentException if the width and height of each + * image in the images array is not equal to the width and height of this + * ImageComponent object. + * + * @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 + * The image class is set to ImageClass.NIO_IMAGE_BUFFER. + * + * @param images array of NioImageBuffer objects containing the image. + * The size (width and height) of each image must be the same as the + * size of the image component, and the length of the images array + * must equal the depth of the image component. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalStateException if this ImageComponent object + * is not yUp. + * + * @exception IllegalArgumentException if the length of the images array is + * not equal to the depth of this ImageComponent object. + * + * @exception IllegalArgumentException if the width and height of each + * image in the images array is not equal to the width and height of this + * ImageComponent object. + * + * @exception UnsupportedOperationException this method is not supported + * for Java 3D 1.5. + * + * @since Java 3D 1.5 + */ + public void set(NioImageBuffer[] images) { + + throw new UnsupportedOperationException(); + /* + checkForLiveOrCompiled(); + int depth = ((ImageComponent3DRetained)this.retained).getDepth(); + + if (depth != images.length) + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D1")); + for (int i=0; iBY_REFERENCE. + * + * @exception IllegalArgumentException if width or + * height of + * the subregion exceeds the dimension of the image in this object. + * + * @exception IllegalArgumentException if dstX < 0, or + * (dstX + width) > width of this object, or + * dstY < 0, or + * (dstY + height) > height of this object. + * + * @exception IllegalArgumentException if srcX < 0, or + * (srcX + width) > width of the RenderedImage + * object containing the subimage, or + * srcY < 0, or + * (srcY + height) > height of the + * RenderedImage object containing the subimage. + * + * @exception IllegalArgumentException if the specified RenderedImage + * is not compatible with the existing RenderedImage. + * + * @exception IllegalStateException if the image class is not one of: + * ImageClass.BUFFERED_IMAGE or ImageClass.RENDERED_IMAGE. + * + * @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. + *

+ * 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. + *

+ * @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 be less than the depth of this ImageComponent3D 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 + * BY_COPY. + * @exception IllegalArgumentException if width or + * height of + * the subregion exceeds the dimension of the image in this object. + * @exception IllegalArgumentException if x < 0, or + * (x + width) > width of this object, or + * y < 0, or + * (y + height) > height of this object. + * @exception ArrayIndexOutOfBoundsException if index > 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. + */ + @Override + void createRetained() { + this.retained = new ImageComponent3DRetained(); + this.retained.setSource(this); + } + + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + ImageComponent3DRetained rt = (ImageComponent3DRetained) retained; + + ImageComponent3D img = new ImageComponent3D(rt.getFormat(), + rt.width, + rt.height, + rt.depth); + + // XXXX : 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 originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + void duplicateAttributes(NodeComponent originalNodeComponent, + boolean forceDuplicate) { + super.duplicateAttributes(originalNodeComponent, forceDuplicate); + // TODO : Handle NioImageBuffer if its supported. + 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 updateData 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. + *
+ * NOTE: Applications should not 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/javax/media/j3d/ImageComponent3DRetained.java b/src/javax/media/j3d/ImageComponent3DRetained.java new file mode 100644 index 0000000..fd65ad1 --- /dev/null +++ b/src/javax/media/j3d/ImageComponent3DRetained.java @@ -0,0 +1,382 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; + +/** + * This class defines a 3D array of pixels. + * This is used for texture images. + */ + +class ImageComponent3DRetained extends ImageComponentRetained { + + 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 + */ + 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. + */ + void set(int index, BufferedImage image) { + + geomLock.getLock(); + + if(byReference) { + // Fix to issue 488. + setRefImage(image, index); + } + + if(imageData == null) { + // Only do this once, on the first image + // Reset this flag to true, incase it was set to false due to + // the previous image type. + abgrSupported = true; + imageTypeIsSupported = isImageTypeSupported(image); + imageData = createRenderedImageDataObject(null); + } + else { + if(getImageType() != evaluateImageType(image)) { + // TODO need to throw illegal state exception + } + } + + if (imageTypeIsSupported) { + copySupportedImageToImageData(image, index, imageData); + } else { + // image type is unsupported, need to create a supported local copy. + // TODO : borrow code from JAI to convert to right format. + copyUnsupportedImageToImageData(image, index, imageData); + + } + + geomLock.unLock(); + + if (source.isLive()) { + // send a IMAGE_CHANGED message in order to + // notify all the users of the change + sendMessage(IMAGE_CHANGED, null); + } + } + + /** + * 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. + * + void set(int index, NioImageBuffer nioImage) { + + int width = nioImage.getWidth(); + int height = nioImage.getHeight(); + + if (!byReference) { + throw new IllegalArgumentException(J3dI18N.getString("Need_New_Message_XXXXXImageComponent2D7")); + } + if (!yUp) { + throw new IllegalArgumentException(J3dI18N.getString("Need_New_Message_XXXXXImageComponent2D8")); + } + + if (width != this.width) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D2")); + } + if (height != this.height) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D4")); + } + + geomLock.getLock(); + + setImageClass(nioImage); + + // This is a byRef image. + setRefImage(nioImage,0); + + if(imageData == null) { + // Only do this once, on the first image + // Reset this flag to true, incase it was set to false due to + // the previous image type. + abgrSupported = true; + + imageTypeIsSupported = isImageTypeSupported(nioImage); + + + // TODO : Need to handle null .... + imageData = createNioImageBufferDataObject(null); + } + else { + + //if(getImageType() != evaluateImageType(image)) { + // TODO need to throw illegal state exception + //} + + } + + if (imageTypeIsSupported) { + // TODO : Need to handle this ..... case .... + // copySupportedImageToImageData(image, index, imageData); + } else { + // System.err.println("Image format is unsupported -- illogical case"); + throw new AssertionError(); + } + + geomLock.unLock(); + + if (source.isLive()) { + // send a IMAGE_CHANGED message in order to + // notify all the users of the change + sendMessage(IMAGE_CHANGED, null); + } + } + */ + + void set(int index, RenderedImage image) { + + int width = image.getWidth(); + int height = image.getHeight(); + + if (width != this.width) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D2")); + } + if (height != this.height) { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent3D4")); + } + + 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 + */ + RenderedImage[] getRenderedImage() { + int i; + RenderedImage bi[] = new RenderedImage[depth]; + + if (!byReference) { + for (i=0; i userList = new ArrayList(); + + /** + * Retrieves the width of this image component object. + * @return the width of this image component object + */ + int getWidth() { + return width; + } + + /** + * Retrieves the height of this image component object. + * @return the height of this image component object + */ + int getHeight() { + return height; + } + + /** + * Retrieves the apiFormat of this image component object. + * + * @return the apiFormat of this image component object + */ + int getFormat() { + return apiFormat; + } + + void setFormat(int format) { + this.apiFormat = format; + } + + void setByReference(boolean byReference) { + this.byReference = byReference; + } + + boolean isByReference() { + return byReference; + } + + void setYUp( boolean yUp) { + this.yUp = yUp; + } + + boolean isYUp() { + return yUp; + } + + int getUnitsPerPixel() { + return unitsPerPixel; + } + + void setUnitsPerPixel(int ipp) { + unitsPerPixel = ipp; + } + + ImageComponent.ImageClass getImageClass() { + return imageClass; + } + + void setImageClass(RenderedImage image) { + if(image instanceof BufferedImage) { + imageClass = ImageComponent.ImageClass.BUFFERED_IMAGE; + } else { + imageClass = ImageComponent.ImageClass.RENDERED_IMAGE; + } + } + + void setImageClass(NioImageBuffer image) { + imageClass = ImageComponent.ImageClass.NIO_IMAGE_BUFFER; + } + + void setEnforceNonPowerOfTwoSupport(boolean npot) { + this.enforceNonPowerOfTwoSupport = npot; + } + + void setUsedByOffScreen(boolean used) { + usedByOffScreenCanvas = used; + } + + boolean getUsedByOffScreen() { + return usedByOffScreenCanvas; + } + + int getNumberOfComponents() { + return numberOfComponents; + } + + void setNumberOfComponents(int numberOfComponents) { + this.numberOfComponents = numberOfComponents; + } + + int getImageDataTypeIntValue() { + int idtValue = -1; + switch(imageData.imageDataType) { + case TYPE_BYTE_ARRAY: + idtValue = IMAGE_DATA_TYPE_BYTE_ARRAY; + break; + case TYPE_INT_ARRAY: + idtValue = IMAGE_DATA_TYPE_INT_ARRAY; + break; + case TYPE_BYTE_BUFFER: + idtValue = IMAGE_DATA_TYPE_BYTE_BUFFER; + break; + case TYPE_INT_BUFFER: + idtValue = IMAGE_DATA_TYPE_INT_BUFFER; + break; + default : + assert false; + } + return idtValue; + + } + + int getImageFormatTypeIntValue(boolean powerOfTwoData) { + int iftValue = -1; + switch(imageFormatType) { + case TYPE_BYTE_BGR: + iftValue = TYPE_BYTE_BGR; + break; + case TYPE_BYTE_RGB: + iftValue = TYPE_BYTE_RGB; + break; + case TYPE_BYTE_ABGR: + iftValue = TYPE_BYTE_ABGR; + break; + case TYPE_BYTE_RGBA: + if((imageDataPowerOfTwo != null) && (powerOfTwoData)) { + iftValue = TYPE_BYTE_ABGR; + } + else { + iftValue = TYPE_BYTE_RGBA; + } + break; + case TYPE_BYTE_LA: + iftValue = TYPE_BYTE_LA; + break; + case TYPE_BYTE_GRAY: + iftValue = TYPE_BYTE_GRAY; + break; + case TYPE_USHORT_GRAY: + iftValue = TYPE_USHORT_GRAY; + break; + case TYPE_INT_BGR: + iftValue = TYPE_INT_BGR; + break; + case TYPE_INT_RGB: + iftValue = TYPE_INT_RGB; + break; + case TYPE_INT_ARGB: + iftValue = TYPE_INT_ARGB; + break; + default: + throw new AssertionError(); + } + return iftValue; + } + + // Note: This method for RenderedImage, can't be used by NioImageBuffer. + int getImageType() { + return imageType; + } + + void setImageFormatType(ImageFormatType ift) { + this.imageFormatType = ift; + } + + ImageFormatType getImageFormatType() { + return this.imageFormatType; + } + + void setRefImage(Object image, int index) { + this.refImage[index] = image; + } + + Object getRefImage(int index) { + return this.refImage[index]; + } + + ImageData getImageData(boolean npotSupportNeeded) { + if(npotSupportNeeded) { + assert enforceNonPowerOfTwoSupport; + if(imageDataPowerOfTwo != null) { + return imageDataPowerOfTwo; + } + } + return imageData; + } + + boolean useBilinearFilter() { + if(imageDataPowerOfTwo != null) { + return true; + } + + return false; + } + + boolean isImageTypeSupported() { + return imageTypeIsSupported; + } + + /** + * 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 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 + case ImageComponent.FORMAT_RGB4: // Need to be Deprecated + case ImageComponent.FORMAT_RGB5: // Need to be Deprecated + case ImageComponent.FORMAT_R3_G3_B2: // Need to be Deprecated + numberOfComponents = 3; + break; + case ImageComponent.FORMAT_RGBA:// same as ImageComponent.FORMAT_RGBA8 + case ImageComponent.FORMAT_RGB5_A1: // Need to be Deprecated + case ImageComponent.FORMAT_RGBA4: // Need to be Deprecated + numberOfComponents = 4; + break; + case ImageComponent.FORMAT_LUM4_ALPHA4: // Need to be Deprecated + case ImageComponent.FORMAT_LUM8_ALPHA8: + numberOfComponents = 2; + break; + case ImageComponent.FORMAT_CHANNEL8: + numberOfComponents = 1; + break; + default: + throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained3")); + } + + this.setFormat(format); + this.width = width; + this.height = height; + this.depth = depth; + refImage = new Object[depth]; + } + + int evaluateImageType(RenderedImage ri) { + int imageType = BufferedImage.TYPE_CUSTOM; + + if (ri instanceof BufferedImage) { + imageType = ((BufferedImage)ri).getType(); + + if(imageType != BufferedImage.TYPE_CUSTOM) { + return imageType; + } + } + else { + // Fix to Issue 412. Force copy for RenderedImage of type not equal to BufferedImage. + return imageType; + } + + // System.err.println("This is a RenderedImage or BufferedImage with TYPE_CUSTOM. It imageType classification may not be correct."); + ColorModel cm = ri.getColorModel(); + ColorSpace cs = cm.getColorSpace(); + SampleModel sm = ri.getSampleModel(); + + int csType = cs.getType(); + boolean isAlphaPre = cm.isAlphaPremultiplied(); + + + 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 if(csType == ColorSpace.TYPE_RGB) { + int comparedBit = 0; + int smDataType = sm.getDataType(); + if(smDataType == DataBuffer.TYPE_BYTE) { + comparedBit = 8; + } else if(smDataType == DataBuffer.TYPE_INT) { + comparedBit = 32; + } + + if(comparedBit != 0) { + int numBands = sm.getNumBands(); + if (cm instanceof ComponentColorModel && + sm instanceof PixelInterleavedSampleModel) { + PixelInterleavedSampleModel csm = + (PixelInterleavedSampleModel) sm; + int[] offs = csm.getBandOffsets(); + ComponentColorModel ccm = (ComponentColorModel)cm; + int[] nBits = ccm.getComponentSize(); + boolean isNBit = true; + for (int i=0; i < numBands; i++) { + if (nBits[i] != comparedBit) { + isNBit = false; + break; + } + } + + // Handle TYPE_BYTE + if( comparedBit == 8) { + if (isNBit && + 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); + } + } + } + //Handle TYPE_INT + else { + if (isNBit) { + if (numBands == 3) { + if(offs[0] == numBands-1 && + offs[1] == numBands-2 && + offs[2] == numBands-3) { + imageType = BufferedImage.TYPE_INT_BGR; + } else if(offs[0] == 0 && + offs[1] == 1 && + offs[2] == 2) { + imageType = BufferedImage.TYPE_INT_RGB; + } + } else if(offs[0] == 3 && + offs[1] == 0 && + offs[2] == 1 && + offs[3] == 2) { + imageType = (isAlphaPre + ? BufferedImage.TYPE_INT_ARGB_PRE + : BufferedImage.TYPE_INT_ARGB); + } + } + } + } + } + } + + return imageType; + } + + // Assume ri's imageType is BufferedImage.TYPE_CUSTOM + boolean is3ByteRGB(RenderedImage ri) { + boolean value = false; + int i; + 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 ((numBands == 3) && (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) { + value = true; + } + } + } + } + return value; + } + + // Assume ri's imageType is BufferedImage.TYPE_CUSTOM + boolean is4ByteRGBA(RenderedImage ri) { + boolean value = false; + int i; + 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 ((numBands == 4) && (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 && + offs[3] == 3 && !isAlphaPre) { + value = true; + } + } + } + } + return value; + } + + // Note: This method for RenderedImage, can't be used by NioImageBuffer. + /* Check if sub-image type matches image type */ + boolean isSubImageTypeEqual(RenderedImage ri) { + int subImageType = evaluateImageType(ri); + + // This test is likely too loose, but the specification isn't clear either. + // Assuming TYPE_CUSTOM of sub-image == the TYPE_CUSTOM of existing image. + if(imageType == subImageType) { + return true; + } else { + return false; + } + + } + + // This method only support caller of offScreenBuffer and readRaster. + void createBlankImageData() { + + assert (imageData == null); + + switch(numberOfComponents) { + case 4: + imageType = BufferedImage.TYPE_INT_ARGB; + imageFormatType = ImageFormatType.TYPE_INT_ARGB; + unitsPerPixel = 1; + break; + + case 3: + imageType = BufferedImage.TYPE_INT_RGB; + imageFormatType = ImageFormatType.TYPE_INT_RGB; + unitsPerPixel = 1; + break; + default: + // Only valid for 3 and 4 channel case. ( Read back from framebuffer ) + assert false; + } + + imageTypeIsSupported = true; + imageData = createRenderedImageDataObject(null); + + } + + // This method will set imageType, imageFormatType, and unitsPerPixel + // as it evaluates NioImageBuffer is supported. It will also reset + // abgrSupported. + boolean isImageTypeSupported(NioImageBuffer nioImgBuf) { + + boolean isSupported = true; + NioImageBuffer.ImageType nioImageType = nioImgBuf.getImageType(); + + switch(numberOfComponents) { + case 4: + switch(nioImageType) { + case TYPE_4BYTE_ABGR: + // TODO : This approach will lead to a very slow path + // for unsupported case. + if(abgrSupported) { + imageFormatType = ImageFormatType.TYPE_BYTE_ABGR; + } else { + // Unsupported format on HW, switch to slow copy. + imageFormatType = ImageFormatType.TYPE_BYTE_RGBA; + isSupported = false; + } + unitsPerPixel = 4; + break; + case TYPE_4BYTE_RGBA: + imageFormatType = ImageFormatType.TYPE_BYTE_RGBA; + unitsPerPixel = 4; + break; + case TYPE_INT_ARGB: + imageFormatType = ImageFormatType.TYPE_INT_ARGB; + unitsPerPixel = 1; + break; + default: + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5")); + + } + break; + case 3: + switch(nioImageType) { + case TYPE_3BYTE_BGR: + imageFormatType = ImageFormatType.TYPE_BYTE_BGR; + unitsPerPixel = 3; + break; + case TYPE_3BYTE_RGB: + imageFormatType = ImageFormatType.TYPE_BYTE_RGB; + unitsPerPixel = 3; + break; + case TYPE_INT_BGR: + imageFormatType = ImageFormatType.TYPE_INT_BGR; + unitsPerPixel = 1; + break; + case TYPE_INT_RGB: + imageFormatType = ImageFormatType.TYPE_INT_RGB; + unitsPerPixel = 1; + break; + default: + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5")); + } + break; + + case 2: + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5")); + case 1: + if(nioImageType == NioImageBuffer.ImageType.TYPE_BYTE_GRAY) { + imageFormatType = ImageFormatType.TYPE_BYTE_GRAY; + unitsPerPixel = 1; + } else { + throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5")); + } + break; + + default: + throw new AssertionError(); + } + + return isSupported; + } + + // This method will set imageType, imageFormatType, and unitsPerPixel + // as it evaluates RenderedImage is supported. It will also reset + // abgrSupported. + boolean isImageTypeSupported(RenderedImage ri) { + + boolean isSupported = true; + imageType = evaluateImageType(ri); + + switch(numberOfComponents) { + case 4: + if(imageType == BufferedImage.TYPE_4BYTE_ABGR) { + + // TODO : This approach will lead to a very slow path + // for unsupported case. + if(abgrSupported) { + imageFormatType = ImageFormatType.TYPE_BYTE_ABGR; + } else { + // Unsupported format on HW, switch to slow copy. + imageFormatType = ImageFormatType.TYPE_BYTE_RGBA; + isSupported = false; + } + unitsPerPixel = 4; + } else if(imageType == BufferedImage.TYPE_INT_ARGB) { + imageFormatType = ImageFormatType.TYPE_INT_ARGB; + unitsPerPixel = 1; + } else if(is4ByteRGBA(ri)) { + imageFormatType = ImageFormatType.TYPE_BYTE_RGBA; + unitsPerPixel = 4; + } else { + // System.err.println("Image format is unsupported --- Case 4"); + // Convert unsupported format to TYPE_BYTE_RGBA. + imageFormatType = ImageFormatType.TYPE_BYTE_RGBA; + isSupported = false; + unitsPerPixel = 4; + } + break; + + case 3: + if(imageType == BufferedImage.TYPE_3BYTE_BGR) { + imageFormatType = ImageFormatType.TYPE_BYTE_BGR; + unitsPerPixel = 3; + } else if(imageType == BufferedImage.TYPE_INT_BGR) { + imageFormatType = ImageFormatType.TYPE_INT_BGR; + unitsPerPixel = 1; + } else if(imageType == BufferedImage.TYPE_INT_RGB) { + imageFormatType = ImageFormatType.TYPE_INT_RGB; + unitsPerPixel = 1; + } else if(is3ByteRGB(ri)) { + imageFormatType = ImageFormatType.TYPE_BYTE_RGB; + unitsPerPixel = 3; + } else { + // System.err.println("Image format is unsupported --- Case 3"); + // Convert unsupported format to TYPE_BYTE_RGB. + imageFormatType = ImageFormatType.TYPE_BYTE_RGB; + isSupported = false; + unitsPerPixel = 3; + } + break; + + case 2: + // System.err.println("Image format is unsupported --- Case 2"); + // Convert unsupported format to TYPE_BYTE_LA. + imageFormatType = ImageFormatType.TYPE_BYTE_LA; + isSupported = false; + unitsPerPixel = 2; + break; + + case 1: + if(imageType == BufferedImage.TYPE_BYTE_GRAY) { + imageFormatType = ImageFormatType.TYPE_BYTE_GRAY; + unitsPerPixel = 1; + } else { + // System.err.println("Image format is unsupported --- Case 1"); + // Convert unsupported format to TYPE_BYTE_GRAY. + imageFormatType = ImageFormatType.TYPE_BYTE_GRAY; + isSupported = false; + unitsPerPixel = 1; + } + break; + + default: + throw new AssertionError(); + } + + return isSupported; + } + + /* + * This method assume that the following members have been initialized : + * width, height, depth, imageFormatType, and unitsPerPixel. + */ + ImageData createNioImageBufferDataObject(NioImageBuffer nioImageBuffer) { + + switch(imageFormatType) { + case TYPE_BYTE_GRAY: + case TYPE_BYTE_LA: + case TYPE_BYTE_RGB: + case TYPE_BYTE_BGR: + case TYPE_BYTE_RGBA: + case TYPE_BYTE_ABGR: + if(nioImageBuffer != null) { + return new ImageData(ImageDataType.TYPE_BYTE_BUFFER, + width * height * depth * unitsPerPixel, + width, height, nioImageBuffer); + } else { + // This is needed only if abgr is unsupported. + return new ImageData(ImageDataType.TYPE_BYTE_BUFFER, + width * height * depth * unitsPerPixel, + width, height); + } + case TYPE_INT_RGB: + case TYPE_INT_BGR: + case TYPE_INT_ARGB: + return new ImageData(ImageDataType.TYPE_INT_BUFFER, + width * height * depth * unitsPerPixel, + width, height, nioImageBuffer); + default: + throw new AssertionError(); + } + } + + /* + * This method assume that the following members have been initialized : + * depth, imageType, imageFormatType, and unitsPerPixel. + */ + ImageData createRenderedImageDataObject(RenderedImage byRefImage, int dataWidth, int dataHeight) { + switch(imageFormatType) { + case TYPE_BYTE_GRAY: + case TYPE_BYTE_LA: + case TYPE_BYTE_RGB: + case TYPE_BYTE_BGR: + case TYPE_BYTE_RGBA: + case TYPE_BYTE_ABGR: + if(byRefImage != null) { + return new ImageData(ImageDataType.TYPE_BYTE_ARRAY, + dataWidth * dataHeight * depth * unitsPerPixel, + dataWidth, dataHeight, byRefImage); + } else { + return new ImageData(ImageDataType.TYPE_BYTE_ARRAY, + dataWidth * dataHeight * depth * unitsPerPixel, + dataWidth, dataHeight); + } + case TYPE_INT_RGB: + case TYPE_INT_BGR: + case TYPE_INT_ARGB: + if(byRefImage != null) { + return new ImageData(ImageDataType.TYPE_INT_ARRAY, + dataWidth * dataHeight * depth * unitsPerPixel, + dataWidth, dataHeight, byRefImage); + } else { + return new ImageData(ImageDataType.TYPE_INT_ARRAY, + dataWidth * dataHeight * depth * unitsPerPixel, + dataWidth, dataHeight); + } + default: + throw new AssertionError(); + } + } + + private void updateImageDataPowerOfTwo(int depthIndex) { + assert enforceNonPowerOfTwoSupport; + BufferedImage bufImage = imageData.createBufferedImage(depthIndex); + BufferedImage scaledImg = powerOfTwoATOp.filter(bufImage, null); + copySupportedImageToImageData(scaledImg, 0, imageDataPowerOfTwo); + } + + /* + * This method assume that the following members have been initialized : + * width, height, depth, imageType, imageFormatType, and bytesPerPixel. + */ + ImageData createRenderedImageDataObject(RenderedImage byRefImage) { + + return createRenderedImageDataObject(byRefImage, width, height); + + } + + + /** + * Copy specified region of image data from RenderedImage to + * ImageComponent's imageData object + */ + void copySupportedImageToImageData(RenderedImage ri, int srcX, int srcY, + int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) { + + assert (data != null); + + 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; + } + + int offset; + int w, h, i, j, m, n; + int dstBegin; + Object pixel = null; + java.awt.image.Raster ras; + int lineUnits; // nbytes per line in dst image buffer + int sign; // -1 for going down + int dstLineUnits; // sign * lineUnits + int tileStart; // destination buffer offset + // at the next left most tile + + byte[] dstByteBuffer = null; + int[] dstIntBuffer = null; + + switch(data.getType()) { + case TYPE_BYTE_ARRAY: + dstByteBuffer = data.getAsByteArray(); + break; + case TYPE_INT_ARRAY: + dstIntBuffer = data.getAsIntArray(); + break; + default: + assert false; + } + + int dataWidth = data.dataWidth; + int dataHeight = data.dataHeight; + + lineUnits = dataWidth * unitsPerPixel; + if (yUp) { + // destination buffer offset + tileStart = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel; + sign = 1; + dstLineUnits = lineUnits; + } else { + // destination buffer offset + tileStart = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel; + sign = -1; + dstLineUnits = -lineUnits; + } + +/* + System.err.println("tileStart= " + tileStart + " dstLineUnits= " + dstLineUnits); + System.err.println("startw= " + startw); + */ + + // allocate memory for a pixel + ras = ri.getTile(minTileX,minTileY); + pixel = getDataElementBuffer(ras); + + int srcOffset, dstOffset; + int tileLineUnits = tilew * unitsPerPixel; + int copyUnits; + + 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); + + srcOffset = (y * tilew + x) * unitsPerPixel; + dstOffset = dstBegin; + + copyUnits = curw * unitsPerPixel; + + //System.err.println("curh = "+curh+" curw = "+curw); + //System.err.println("x = "+x+" y = "+y); + + switch(data.getType()) { + case TYPE_BYTE_ARRAY: + byte[] srcByteBuffer = ((DataBufferByte)ras.getDataBuffer()).getData(); + for (h = 0; h < curh; h++) { + System.arraycopy(srcByteBuffer, srcOffset, dstByteBuffer, dstOffset, + copyUnits); + srcOffset += tileLineUnits; + dstOffset += dstLineUnits; + } + break; + case TYPE_INT_ARRAY: + int[] srcIntBuffer = ((DataBufferInt)ras.getDataBuffer()).getData(); + for (h = 0; h < curh; h++) { + System.arraycopy(srcIntBuffer, srcOffset, dstIntBuffer, dstOffset, + copyUnits); + srcOffset += tileLineUnits; + dstOffset += dstLineUnits; + } + break; + default: + assert false; + } + + // advance the destination buffer offset + dstBegin += curw * unitsPerPixel; + + // 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 += dataWidth * unitsPerPixel * 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; + } + } + + if((imageData == data) && (imageDataPowerOfTwo != null)) { + updateImageDataPowerOfTwo(depthIndex); + } + } + + // Quick line by line copy + void copyImageLineByLine(BufferedImage bi, int srcX, int srcY, + int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) { + + assert (data != null); + + int h; + int rowBegin, // src begin row index + srcBegin, // src begin offset + dstBegin; // dst begin offset + + int dataWidth = data.dataWidth; + int dataHeight = data.dataHeight; + int dstUnitsPerRow = dataWidth * unitsPerPixel; // bytes per row in dst image + rowBegin = srcY; + + if (yUp) { + dstBegin = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel; + } else { + dstBegin = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel; + dstUnitsPerRow = - 1 * dstUnitsPerRow; + } + + int copyUnits = copyWidth * unitsPerPixel; + int srcWidth = bi.getWidth(); + int srcUnitsPerRow = srcWidth * unitsPerPixel; + srcBegin = (rowBegin * srcWidth + srcX) * unitsPerPixel; + + switch(data.getType()) { + case TYPE_BYTE_ARRAY: + byte[] srcByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + byte[] dstByteBuffer = data.getAsByteArray(); + for (h = 0; h < copyHeight; h++) { + System.arraycopy(srcByteBuffer, srcBegin, dstByteBuffer, dstBegin, copyUnits); + dstBegin += dstUnitsPerRow; + srcBegin += srcUnitsPerRow; + } + break; + + case TYPE_INT_ARRAY: + int[] srcIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); + int[] dstIntBuffer = data.getAsIntArray(); + for (h = 0; h < copyHeight; h++) { + System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, dstBegin, copyUnits); + dstBegin += dstUnitsPerRow; + srcBegin += srcUnitsPerRow; + } + break; + default: + assert false; + } + + if((imageData == data) && (imageDataPowerOfTwo != null)) { + updateImageDataPowerOfTwo(depthIndex); + } + } + + // Quick block copy for yUp image + void copyImageByBlock(BufferedImage bi, int depthIndex, ImageData data) { + + assert ((data != null) && yUp); + + int dataWidth = data.dataWidth; + int dataHeight = data.dataHeight; + + int dstBegin; // dst begin offset + dstBegin = depthIndex * dataWidth * dataHeight * unitsPerPixel; + + switch(imageData.getType()) { + case TYPE_BYTE_ARRAY: + byte[] srcByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + byte[] dstByteBuffer = data.getAsByteArray(); + System.arraycopy(srcByteBuffer, 0, dstByteBuffer, dstBegin, (dataWidth * dataHeight * unitsPerPixel)); + break; + case TYPE_INT_ARRAY: + int[] srcIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); + int[] dstIntBuffer = data.getAsIntArray(); + System.arraycopy(srcIntBuffer, 0, dstIntBuffer, dstBegin, (dataWidth * dataHeight * unitsPerPixel)); + break; + default: + assert false; + } + + if((imageData == data) && (imageDataPowerOfTwo != null)) { + updateImageDataPowerOfTwo(depthIndex); + } + + } + + /** + * copy complete region of a RenderedImage to ImageComponent's imageData object. + */ + void copySupportedImageToImageData(RenderedImage ri, int depthIndex, ImageData data) { + + if (ri instanceof BufferedImage) { + if(yUp) { + /* Use quick block copy when ( format is OK, Yup is true, and byRef is false). */ + // System.err.println("ImageComponentRetained.copySupportedImageToImageData() : (imageTypeSupported && !byReference && yUp) --- (2 BI)"); + copyImageByBlock((BufferedImage)ri, depthIndex, data); + } else { + /* Use quick inverse line by line copy when (format is OK and Yup is false). */ + // System.err.println("ImageComponentRetained.copySupportedImageToImageData() : (imageTypeSupported && !yUp) --- (3 BI)"); + copyImageLineByLine((BufferedImage)ri, 0, 0, 0, 0, depthIndex, data.dataWidth, data.dataHeight, data); + } + } else { + // System.err.println("ImageComponentRetained.copySupportedImageToImageData() : (imageTypeSupported && !byReference ) --- (2 RI)"); + copySupportedImageToImageData(ri, ri.getMinX(), ri.getMinY(), 0, 0, depthIndex, data.dataWidth, data.dataHeight, data); + + /* + * An alternative approach. + * + // Create a buffered image from renderImage + ColorModel cm = ri.getColorModel(); + WritableRaster wRaster = ri.copyData(null); + BufferedImage bi = new BufferedImage(cm, + wRaster, + cm.isAlphaPremultiplied() + ,null); + + copySupportedImageToImageData((BufferedImage)ri, 0, 0, 0, 0, depthIndex, data.dataWidth, data.dataHeight, data); + + * + * + */ + } + } + + /* + * copy the complete unsupported NioImageBuffer into a supported BYTE_BUFFER format + */ + void copyUnsupportedNioImageToImageData(NioImageBuffer nioImage, int srcX, int srcY, + int dstX, int dstY, int copyWidth, int copyHeight, ImageData iData) { + + if (MasterControl.isDevLoggable(Level.INFO)) { + MasterControl.getDevLogger().info("ImageComponent - Copying Unsupported NioImage, use a different image type"); + } + + assert (iData.getType() == ImageDataType.TYPE_BYTE_BUFFER); + assert (getImageFormatType() == ImageFormatType.TYPE_BYTE_RGBA); + + int length = copyWidth * copyHeight; + ByteBuffer srcBuffer = (ByteBuffer) nioImage.getDataBuffer(); + srcBuffer.rewind(); + ByteBuffer dstBuffer = iData.getAsByteBuffer(); + dstBuffer.rewind(); + + // Do copy and swap. + for(int i = 0; i < length; i +=4) { + dstBuffer.put(i, srcBuffer.get(i+3)); + dstBuffer.put(i+1, srcBuffer.get(i+2)); + dstBuffer.put(i+2, srcBuffer.get(i+1)); + dstBuffer.put(i+3, srcBuffer.get(i)); + } + } + + /* + * copy the complete unsupported image into a supported BYTE_ARRAY format + */ + void copyUnsupportedImageToImageData(RenderedImage ri, int depthIndex, ImageData data) { + + assert (data.getType() == ImageDataType.TYPE_BYTE_ARRAY); + + if (MasterControl.isDevLoggable(Level.INFO)) { + MasterControl.getDevLogger().info("ImageComponent - Copying Unsupported Image, use a different image type"); + } + + if (ri instanceof BufferedImage) { + copyUnsupportedImageToImageData((BufferedImage)ri, 0, 0, 0, 0, + depthIndex, data.dataWidth, data.dataHeight, data); + } else { + copyUnsupportedImageToImageData(ri, ri.getMinX(), ri.getMinY(), + 0, 0, depthIndex, data.dataWidth, data.dataHeight, data); + } + } + + void copyUnsupportedImageToImageData(BufferedImage bi, int srcX, int srcY, + int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) { + + 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; + + rowBegin = srcY; + rowInc = 1; + + assert (data != null); + + int dataWidth = data.dataWidth; + int dataHeight = data.dataHeight; + int dstBytesPerRow = dataWidth * unitsPerPixel; // bytes per row in dst image + + if (yUp) { + dstBegin = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel; + } else { + dstBegin = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel; + dstBytesPerRow = - 1 * dstBytesPerRow; + } + + WritableRaster ras = bi.getRaster(); + ColorModel cm = bi.getColorModel(); + Object pixel = getDataElementBuffer(ras); + + byte[] dstBuffer = data.getAsByteArray(); + + switch(numberOfComponents) { + case 4: { + for (row = rowBegin, h = 0; + h < copyHeight; h++, row += rowInc) { + j = dstBegin; + for (w = srcX; w < (copyWidth + srcX); w++) { + ras.getDataElements(w, row, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + dstBuffer[j++] = (byte)cm.getGreen(pixel); + dstBuffer[j++] = (byte)cm.getBlue(pixel); + dstBuffer[j++] = (byte)cm.getAlpha(pixel); + } + dstBegin += dstBytesPerRow; + } + } + break; + + case 3: { + for (row = rowBegin, h = 0; + h < copyHeight; h++, row += rowInc) { + j = dstBegin; + for (w = srcX; w < (copyWidth + srcX); w++) { + ras.getDataElements(w, row, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + dstBuffer[j++] = (byte)cm.getGreen(pixel); + dstBuffer[j++] = (byte)cm.getBlue(pixel); + } + dstBegin += dstBytesPerRow; + } + } + break; + + case 2: { + for (row = rowBegin, h = 0; + h < copyHeight; h++, row += rowInc) { + j = dstBegin; + for (w = srcX; w < (copyWidth + srcX); w++) { + ras.getDataElements(w, row, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + dstBuffer[j++] = (byte)cm.getAlpha(pixel); + } + dstBegin += dstBytesPerRow; + } + } + break; + + case 1: { + for (row = rowBegin, h = 0; + h < copyHeight; h++, row += rowInc) { + j = dstBegin; + for (w = srcX; w < (copyWidth + srcX); w++) { + ras.getDataElements(w, row, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + } + dstBegin += dstBytesPerRow; + } + } + break; + default: + assert false; + } + + if((imageData == data) && (imageDataPowerOfTwo != null)) { + updateImageDataPowerOfTwo(depthIndex); + } + } + + void copyUnsupportedImageToImageData(RenderedImage ri, int srcX, int srcY, + int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) { + + int w, h, i, j, m, n; + int dstBegin; + Object pixel = null; + java.awt.image.Raster ras; + // 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; + } + + assert (data != null); + int dataWidth = data.dataWidth; + int dataHeight = data.dataHeight; + int lineBytes = dataWidth * unitsPerPixel; // nbytes per line in + + if (yUp) { + // destination buffer offset + tileStart = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel; + sign = 1; + dstLineBytes = lineBytes; + } else { + // destination buffer offset + tileStart = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel; + sign = -1; + dstLineBytes = -lineBytes; + } + +/* + System.err.println("tileStart= " + tileStart + " dstLineBytes= " + dstLineBytes); + System.err.println("startw= " + startw); + */ + + // allocate memory for a pixel + ras = ri.getTile(minTileX,minTileY); + pixel = getDataElementBuffer(ras); + byte[] dstBuffer = imageData.getAsByteArray(); + + switch(numberOfComponents) { + case 4: { + // System.err.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.err.println("curh = "+curh+" curw = "+curw); + //System.err.println("x = "+x+" y = "+y); + + for (h = y; h < (y + curh); h++) { + // System.err.println("j = "+j); + for (w = x; w < (x + curw); w++) { + ras.getDataElements(w, h, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + dstBuffer[j++] = (byte)cm.getGreen(pixel); + dstBuffer[j++] = (byte)cm.getBlue(pixel); + dstBuffer[j++] = (byte)cm.getAlpha(pixel); + } + offset += dstLineBytes; + j = dstBegin + offset; + } + + // advance the destination buffer offset + dstBegin += curw * unitsPerPixel; + + // 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 += dataWidth * unitsPerPixel * 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 3: { + 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.err.println("curh = "+curh+" curw = "+curw); + //System.err.println("x = "+x+" y = "+y); + + for (h = y; h < (y + curh); h++) { + // System.err.println("j = "+j); + for (w = x; w < (x + curw); w++) { + ras.getDataElements(w, h, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + dstBuffer[j++] = (byte)cm.getGreen(pixel); + dstBuffer[j++] = (byte)cm.getBlue(pixel); + } + offset += dstLineBytes; + j = dstBegin + offset; + } + + // advance the destination buffer offset + dstBegin += curw * unitsPerPixel; + + // 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 += dataWidth * unitsPerPixel * 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 2: { + 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.err.println("curh = "+curh+" curw = "+curw); + //System.err.println("x = "+x+" y = "+y); + + for (h = y; h < (y + curh); h++) { + // System.err.println("j = "+j); + for (w = x; w < (x + curw); w++) { + ras.getDataElements(w, h, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + dstBuffer[j++] = (byte)cm.getAlpha(pixel); + } + offset += dstLineBytes; + j = dstBegin + offset; + } + + // advance the destination buffer offset + dstBegin += curw * unitsPerPixel; + + // 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 += dataWidth * unitsPerPixel * 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 1: { + 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.err.println("curh = "+curh+" curw = "+curw); + //System.err.println("x = "+x+" y = "+y); + + for (h = y; h < (y + curh); h++) { + // System.err.println("j = "+j); + for (w = x; w < (x + curw); w++) { + ras.getDataElements(w, h, pixel); + dstBuffer[j++] = (byte)cm.getRed(pixel); + } + offset += dstLineBytes; + j = dstBegin + offset; + } + + // advance the destination buffer offset + dstBegin += curw * unitsPerPixel; + + // 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 += dataWidth * unitsPerPixel * 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: + assert false; + } + + if((imageData == data) && (imageDataPowerOfTwo != null)) { + updateImageDataPowerOfTwo(depthIndex); + } + } + + + void evaluateExtensions(Canvas3D canvas) { + // Issue 366: need to synchronize since it could be called concurrently + // from multiple renderers (and maybe the renderer(s) and renderbin) + synchronized (evaluateExtLock) { + // For performance reason the ordering of the following 2 statements is intentional. + // So that we only need to do format conversion for imageData only + evaluateExtABGR(canvas.extensionsSupported); + evaluateExtNonPowerOfTwo(canvas.textureExtendedFeatures); + } + + } + + + void evaluateExtABGR(int ext) { + + + // If abgrSupported is false, a copy has been created so + // we don't have to check again. + if(!abgrSupported) { + return; + } + + if(getImageFormatType() != ImageFormatType.TYPE_BYTE_ABGR) { + return; + } + + if((ext & Canvas3D.EXT_ABGR) != 0) { + return; + } + + // ABGR is unsupported, set flag to false. + abgrSupported = false; + convertImageDataFromABGRToRGBA(); + + } + + 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; + } + } + } + + private int getCeilPowerOf2(int value) { + + if (value < 1) + return value; + + int powerValue = 1; + for (;;) { + powerValue *= 2; + if (value <= powerValue) { + // Found max bound of power + return powerValue; + } + } + } + + void evaluateExtNonPowerOfTwo(int ext) { + // Only need to enforce for Raster or Background. + if(!enforceNonPowerOfTwoSupport) { + return; + } + + // If npotSupported is false, a copy power of two image has been created + // so we don't have to check again. + if(!npotSupported) { + return; + } + + if (imageData == null && !isByReference()) { + return; + } + + if((ext & Canvas3D.TEXTURE_NON_POWER_OF_TWO) != 0) { + return; + } + + // NPOT is unsupported, set flag to false. + npotSupported = false; + + int npotWidth; + int npotHeight; + // Always scale up if image size is smaller 512*512. + if((width * height) < IMAGE_SIZE_512X512) { + npotWidth = getCeilPowerOf2(width); + npotHeight = getCeilPowerOf2(height); + } else { + npotWidth = getClosestPowerOf2(width); + npotHeight = getClosestPowerOf2(height); + } + + // System.err.println("width " + width + " height " + height + " npotWidth " + npotWidth + " npotHeight " + npotHeight); + + float xScale = (float)npotWidth/(float)width; + float yScale = (float)npotHeight/(float)height; + + // scale if scales aren't 1.0 + if (!(xScale == 1.0f && yScale == 1.0f)) { + + if (imageData == null) { + // This is a byRef, support format and is a RenderedImage case. + // See ImageComponent2DRetained.set(RenderedImage image) + RenderedImage ri = (RenderedImage) getRefImage(0); + + assert !(ri instanceof BufferedImage); + + // Create a buffered image from renderImage + ColorModel cm = ri.getColorModel(); + WritableRaster wRaster = ri.copyData(null); + ri = new BufferedImage(cm, + wRaster, + cm.isAlphaPremultiplied() + ,null); + + + // Create image data object with buffer for image. */ + imageData = createRenderedImageDataObject(null); + copySupportedImageToImageData(ri, 0, imageData); + + } + + assert imageData != null; + + // Create a supported BufferedImage type. + BufferedImage bi = imageData.createBufferedImage(0); + + int imageType = bi.getType(); + BufferedImage scaledImg = new BufferedImage(npotWidth, npotHeight, imageType); + + AffineTransform at = AffineTransform.getScaleInstance(xScale, + yScale); + + powerOfTwoATOp = new AffineTransformOp(at, + AffineTransformOp.TYPE_BILINEAR); + + powerOfTwoATOp.filter(bi, scaledImg); + + // System.err.println("bi " + bi.getColorModel()); + // System.err.println("scaledImg " + scaledImg.getColorModel()); + + imageDataPowerOfTwo = createRenderedImageDataObject(null, npotWidth, npotHeight); + // Since bi is created from imageData, it's imageType is supported. + copySupportedImageToImageData(scaledImg, 0, imageDataPowerOfTwo); + + } else { + imageDataPowerOfTwo = null; + } + } + + void convertImageDataFromABGRToRGBA() { + + // Unsupported format on HW, switch to slow copy. + imageFormatType = ImageFormatType.TYPE_BYTE_RGBA; + imageTypeIsSupported = false; + + // Only need to convert imageData + imageData.convertFromABGRToRGBA(); + + } + + /** + * Copy supported ImageType from ImageData to the user defined bufferedImage + */ + void copyToRefImage(int depth) { + int h; + int rowBegin, // src begin row index + srcBegin, // src begin offset + dstBegin; // dst begin offset + + // refImage has to be a BufferedImage for off screen and read raster + assert refImage[depth] != null; + assert (refImage[depth] instanceof BufferedImage); + + BufferedImage bi = (BufferedImage)refImage[depth]; + int dstUnitsPerRow = width * unitsPerPixel; // bytes per row in dst image + rowBegin = 0; + + if (yUp) { + dstBegin = (depth * width * height) * unitsPerPixel; + } else { + dstBegin = (depth * width * height + (height - 1) * width ) * unitsPerPixel; + dstUnitsPerRow = - 1 * dstUnitsPerRow; + } + + int scanline = width * unitsPerPixel; + srcBegin = (rowBegin * width ) * unitsPerPixel; + + switch(imageData.getType()) { + case TYPE_BYTE_ARRAY: + byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + byte[] srcByteBuffer = imageData.getAsByteArray(); + for (h = 0; h < height; h++) { + System.arraycopy(srcByteBuffer, srcBegin, dstByteBuffer, dstBegin, scanline); + dstBegin += dstUnitsPerRow; + srcBegin += scanline; + } + break; + + case TYPE_INT_ARRAY: + int[] dstIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); + int[] srcIntBuffer = imageData.getAsIntArray(); + for (h = 0; h < height; h++) { + System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, dstBegin, scanline); + dstBegin += dstUnitsPerRow; + srcBegin += scanline; + } + break; + default: + assert false; + } + + } + + /** + * Copy image to the user defined bufferedImage ( 3 or 4 components only ) + */ + void copyToRefImageWithFormatConversion(int depth) { + int w, h, i, j; + int dstBegin, dstInc, dstIndex, dstIndexInc; + // refImage has to be a BufferedImage for off screen and read raster + assert refImage[depth] != null; + assert (refImage[depth] instanceof BufferedImage); + + BufferedImage bi = (BufferedImage)refImage[depth]; + int biType = bi.getType(); + byte[] buf = imageData.getAsByteArray(); + + // convert from Ydown to Yup for texture + if (!yUp) { + dstInc = -1 * width; + dstBegin = (height - 1) * width; + dstIndex = height -1; + dstIndexInc = -1; + } else { + dstInc = width; + dstBegin = 0; + dstIndex = 0; + dstIndexInc = 1; + } + + 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; + switch (imageFormatType) { + case TYPE_BYTE_RGBA: + 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 TYPE_BYTE_RGB: + for(h = 0; h < height; h++, dstBegin += dstInc) { + i = dstBegin; + for (w = 0; w < width; w++, j+=3, i++) { + intData[i] = (0xff000000 | // a + ((buf[j] &0xff) << 16) | // r + ((buf[j+1] &0xff) << 8) | // g + (buf[j+2] & 0xff)); // b + } + } + break; + default: + assert false; + } + 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; + + //Issue 381: dstBegin contains pixel count, but we are looping over byte count. In case of YDown, it contains a count that is decremented and if we do not multiply, we have an AIOOB thrown at 25% of the copy. + dstBegin <<= 2; + + switch (imageFormatType) { + case TYPE_BYTE_RGBA: + 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 TYPE_BYTE_RGB: + for(h = 0; h < height; h++, dstBegin += (dstInc << 2)) { + i = dstBegin; + for (w = 0; w < width; w++, j+=3) { + byteData[i++] = (byte) 0xff; // a + byteData[i++] = buf[j+2]; // b + byteData[i++] = buf[j+1];// g + byteData[i++] = buf[j]; // r + } + } + break; + default: + assert false; + } + 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; + default: + assert false; + } + + } + + // 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); + } + } + + /* + * + * @exception IllegalSharingException if this image is + * being used by a Canvas3D as an off-screen buffer. + */ + @Override + void setLive(boolean inBackgroundGroup, int refCount) { + // Do illegalSharing check. + if(getUsedByOffScreen()) { + throw new IllegalSharingException(J3dI18N.getString("ImageComponent3")); + } + super.setLive(inBackgroundGroup, refCount); + } + + /** + * ImageComponent object doesn't really have mirror object. + * But it's using the updateMirrorObject interface to propagate + * the changes to the users + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + // System.err.println("ImageComponent.updateMirrorObject"); + if ((component & IMAGE_CHANGED) == 0 && + (component & SUBIMAGE_CHANGED) == 0) + return; + + for (int i = userList.size() - 1; i >= 0; i--) { + NodeComponentRetained user = userList.get(i); + if (user == null) + continue; + + if (user instanceof TextureRetained) { + ((TextureRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value); + } + else if (user instanceof RasterRetained) { + ((RasterRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value); + } + } + } + + final void sendMessage(int attrMask, Object attr) { + + J3dMessage createMessage = new J3dMessage(); + 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); + } + + @Override + 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; + } + + /** + * Wrapper class for image data. + * Currently supports byte array and int array. + * Will eventually support NIO ByteBuffer and IntBuffer. + */ + class ImageData { + + private Object data = null; + private ImageDataType imageDataType = ImageDataType.TYPE_NULL; + private int length = 0; + private boolean dataIsByRef = false; + private int dataWidth, dataHeight; + + /** + * Constructs a new ImageData buffer of the specified type with the + * specified length. + */ + ImageData(ImageDataType imageDataType, int length, int dataWidth, int dataHeight) { + this.imageDataType = imageDataType; + this.length = length; + this.dataWidth = dataWidth; + this.dataHeight = dataHeight; + this.dataIsByRef = false; + + switch (imageDataType) { + case TYPE_BYTE_ARRAY: + data = new byte[length]; + break; + case TYPE_INT_ARRAY: + data = new int[length]; + break; + case TYPE_BYTE_BUFFER: + ByteOrder order = ByteOrder.nativeOrder(); + data = ByteBuffer.allocateDirect(length).order(order); + break; + case TYPE_INT_BUFFER: + default: + throw new AssertionError(); + } + } + + /** + * Constructs a new ImageData buffer of the specified type with the + * specified length and the specified byRefImage as data. + */ + ImageData(ImageDataType imageDataType, int length, int dataWidth, int dataHeight, + Object byRefImage) { + BufferedImage bi; + NioImageBuffer nio; + + this.imageDataType = imageDataType; + this.length = length; + this.dataWidth = dataWidth; + this.dataHeight = dataHeight; + this.dataIsByRef = true; + + switch (imageDataType) { + case TYPE_BYTE_ARRAY: + bi = (BufferedImage) byRefImage; + data = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + break; + case TYPE_INT_ARRAY: + bi = (BufferedImage) byRefImage; + data = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); + break; + case TYPE_BYTE_BUFFER: + case TYPE_INT_BUFFER: + nio = (NioImageBuffer) byRefImage; + data = nio.getDataBuffer(); + break; + default: + throw new AssertionError(); + } + } + + /** + * Constructs a new ImageData buffer from the specified + * object. This object stores a reference to the input image data. + */ + ImageData(Object data, boolean isByRef) { + this.data = data; + dataIsByRef = isByRef; + dataWidth = ((ImageData) data).dataWidth; + dataHeight = ((ImageData) data).dataHeight; + + if (data == null) { + imageDataType = ImageDataType.TYPE_NULL; + length = 0; + } else if (data instanceof byte[]) { + imageDataType = ImageDataType.TYPE_BYTE_ARRAY; + length = ((byte[]) data).length; + } else if (data instanceof int[]) { + imageDataType = ImageDataType.TYPE_INT_ARRAY; + length = ((int[]) data).length; + } else if (data instanceof ByteBuffer) { + imageDataType = ImageDataType.TYPE_BYTE_BUFFER; + length = ((ByteBuffer) data).limit(); + } else if (data instanceof IntBuffer) { + imageDataType = ImageDataType.TYPE_INT_BUFFER; + length = ((IntBuffer) data).limit(); + } else { + assert false; + } + } + + /** + * Returns the type of this DataBuffer. + */ + ImageDataType getType() { + return imageDataType; + } + + /** + * Returns the number of elements in this DataBuffer. + */ + int length() { + return length; + } + + /** + * Returns the width of this DataBuffer. + */ + int getWidth() { + return dataWidth; + } + + /** + * Returns the height of this DataBuffer. + */ + int getHeight() { + return dataHeight; + } + + /** + * Returns this DataBuffer as an Object. + */ + Object get() { + return data; + } + + /** + * Returns is this data is byRef. No internal data is made. + */ + boolean isDataByRef() { + return dataIsByRef; + } + + + /** + * Returns this DataBuffer as a byte array. + */ + byte[] getAsByteArray() { + return (byte[]) data; + } + + /** + * Returns this DataBuffer as an int array. + */ + int[] getAsIntArray() { + return (int[]) data; + } + + /** + * Returns this DataBuffer as an nio ByteBuffer. + */ + ByteBuffer getAsByteBuffer() { + return (ByteBuffer) data; + } + + /** + * Returns this DataBuffer as an nio IntBuffer. + */ + IntBuffer getAsIntBuffer() { + return (IntBuffer) data; + } + + // Handle TYPE_BYTE_LA only + void copyByLineAndExpand(BufferedImage bi, int depthIndex) { + int h; + int srcBegin, // src begin offset + dstBegin; // dst begin offset + + assert (imageData.getType() == ImageDataType.TYPE_BYTE_ARRAY); + assert (imageFormatType == ImageFormatType.TYPE_BYTE_LA); + + int unitsPerRow = width * unitsPerPixel; // bytes per row + int scanline = unitsPerRow; + if (yUp) { + srcBegin = (depthIndex * width * height) * unitsPerPixel; + } else { + srcBegin = (depthIndex * width * height + (height - 1) * width) * unitsPerPixel; + unitsPerRow = - 1 * unitsPerRow; + } + + dstBegin = 0; + // ABGR is 4 bytes per pixel + int dstUnitsPerRow = width * 4; + + byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + byte[] srcByteBuffer = imageData.getAsByteArray(); + for (h = 0; h < height; h++) { + for( int v = 0, w = 0; w < scanline; w += unitsPerPixel, v += 4) { + dstByteBuffer[dstBegin+v] = srcByteBuffer[srcBegin+w+1]; // Alpha + dstByteBuffer[dstBegin+v+1] = 0; + dstByteBuffer[dstBegin+v+2] = 0; + dstByteBuffer[dstBegin+v+3] = srcByteBuffer[srcBegin+w]; // Red + } + + dstBegin += dstUnitsPerRow; + srcBegin += unitsPerRow; + } + + } + + // Quick line by line copy + void copyByLine(BufferedImage bi, int depthIndex, boolean swapNeeded) { + + int h; + int srcBegin, // src begin offset + dstBegin; // dst begin offset + + int unitsPerRow = width * unitsPerPixel; // bytes per row + int copyUnits = unitsPerRow; + if (yUp) { + srcBegin = (depthIndex * width * height) * unitsPerPixel; + } else { + srcBegin = (depthIndex * width * height + (height - 1) * width) * unitsPerPixel; + unitsPerRow = - 1 * unitsPerRow; + } + + dstBegin = 0; + + switch(imageData.getType()) { + case TYPE_BYTE_ARRAY: + byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + byte[] srcByteBuffer = imageData.getAsByteArray(); + for (h = 0; h < height; h++) { + if(!swapNeeded) { + System.arraycopy(srcByteBuffer, srcBegin, + dstByteBuffer, dstBegin, copyUnits); + } else { + if(imageFormatType == ImageFormatType.TYPE_BYTE_RGB) { + assert (unitsPerPixel == 3); + for(int w = 0; w < copyUnits; w += unitsPerPixel) { + dstByteBuffer[dstBegin+w] = srcByteBuffer[srcBegin+w+2]; + dstByteBuffer[dstBegin+w+1] = srcByteBuffer[srcBegin+w+1]; + dstByteBuffer[dstBegin+w+2] = srcByteBuffer[srcBegin+w]; + } + } else if(imageFormatType == ImageFormatType.TYPE_BYTE_RGBA) { + assert (unitsPerPixel == 4); + for(int w = 0; w < copyUnits; w += unitsPerPixel) { + dstByteBuffer[dstBegin+w] = srcByteBuffer[srcBegin+w+3]; + dstByteBuffer[dstBegin+w+1] = srcByteBuffer[srcBegin+w+2]; + dstByteBuffer[dstBegin+w+2] = srcByteBuffer[srcBegin+w+1]; + dstByteBuffer[dstBegin+w+3] = srcByteBuffer[srcBegin+w]; + } + } else { + assert false; + } + } + dstBegin += copyUnits; + srcBegin += unitsPerRow; + } + break; + + // INT case doesn't required to handle swapNeeded + case TYPE_INT_ARRAY: + assert (!swapNeeded); + int[] dstIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); + int[] srcIntBuffer = imageData.getAsIntArray(); + for (h = 0; h < height; h++) { + System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, dstBegin, copyUnits); + dstBegin += copyUnits; + srcBegin += unitsPerRow; + } + break; + default: + assert false; + } + } + + void copyByBlock(BufferedImage bi, int depthIndex) { + // src begin offset + int srcBegin = depthIndex * width * height * unitsPerPixel; + + switch(imageData.getType()) { + case TYPE_BYTE_ARRAY: + byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); + byte[] srcByteBuffer = imageData.getAsByteArray(); + System.arraycopy(srcByteBuffer, srcBegin, dstByteBuffer, 0, (height * width * unitsPerPixel)); + break; + case TYPE_INT_ARRAY: + int[] dstIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); + int[] srcIntBuffer = imageData.getAsIntArray(); + System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, 0, (height * width * unitsPerPixel)); + break; + default: + assert false; + } + } + + // Need to check for imageData is null. if it is null return null. + BufferedImage createBufferedImage(int depthIndex) { + if(data != null) { + int bufferType = BufferedImage.TYPE_CUSTOM; + boolean swapNeeded = false; + + switch(imageFormatType) { + case TYPE_BYTE_BGR: + bufferType = BufferedImage.TYPE_3BYTE_BGR; + break; + case TYPE_BYTE_RGB: + bufferType = BufferedImage.TYPE_3BYTE_BGR; + swapNeeded = true; + break; + case TYPE_BYTE_ABGR: + bufferType = BufferedImage.TYPE_4BYTE_ABGR; + break; + case TYPE_BYTE_RGBA: + bufferType = BufferedImage.TYPE_4BYTE_ABGR; + swapNeeded = true; + break; + // This is a special case. Need to handle separately. + case TYPE_BYTE_LA: + bufferType = BufferedImage.TYPE_4BYTE_ABGR; + break; + case TYPE_BYTE_GRAY: + bufferType = BufferedImage.TYPE_BYTE_GRAY; + break; + case TYPE_INT_BGR: + bufferType = BufferedImage.TYPE_INT_BGR; + break; + case TYPE_INT_RGB: + bufferType = BufferedImage.TYPE_INT_RGB; + break; + case TYPE_INT_ARGB: + bufferType = BufferedImage.TYPE_INT_ARGB; + break; + // Unsupported case, so shouldn't be here. + case TYPE_USHORT_GRAY: + bufferType = BufferedImage.TYPE_USHORT_GRAY; + default: + assert false; + + } + + BufferedImage bi = new BufferedImage(width, height, bufferType); + if((!swapNeeded) && (imageFormatType != ImageFormatType.TYPE_BYTE_LA)) { + if(yUp) { + copyByBlock(bi, depthIndex); + } else { + copyByLine(bi, depthIndex, false); + } + } else if(swapNeeded) { + copyByLine(bi, depthIndex, swapNeeded); + } else if(imageFormatType == ImageFormatType.TYPE_BYTE_LA) { + copyByLineAndExpand(bi, depthIndex); + } else { + assert false; + } + + return bi; + + } + return null; + } + + void convertFromABGRToRGBA() { + int i; + + if(imageDataType == ImageComponentRetained.ImageDataType.TYPE_BYTE_ARRAY) { + // Note : Highly inefficient for depth > 0 case. + // This method doesn't take into account of depth, it is assuming that + // depth == 0, which is true for ImageComponent2D. + byte[] srcBuffer, dstBuffer; + srcBuffer = getAsByteArray(); + + if(dataIsByRef) { + dstBuffer = new byte[length]; + // Do copy and swap. + for(i = 0; i < length; i +=4) { + dstBuffer[i] = srcBuffer[i+3]; + dstBuffer[i+1] = srcBuffer[i+2]; + dstBuffer[i+2] = srcBuffer[i+1]; + dstBuffer[i+3] = srcBuffer[i]; + } + data = dstBuffer; + dataIsByRef = false; + + } else { + byte a, b; + // Do swap in place. + for(i = 0; i < length; i +=4) { + a = srcBuffer[i]; + b = srcBuffer[i+1]; + srcBuffer[i] = srcBuffer[i+3]; + srcBuffer[i+1] = srcBuffer[i+2]; + srcBuffer[i+2] = b; + srcBuffer[i+3] = a; + } + } + } + else if(imageDataType == ImageComponentRetained.ImageDataType.TYPE_BYTE_BUFFER) { + + assert dataIsByRef; + ByteBuffer srcBuffer, dstBuffer; + + srcBuffer = getAsByteBuffer(); + srcBuffer.rewind(); + + ByteOrder order = ByteOrder.nativeOrder(); + dstBuffer = ByteBuffer.allocateDirect(length).order(order); + dstBuffer.rewind(); + + // Do copy and swap. + for(i = 0; i < length; i +=4) { + dstBuffer.put(i, srcBuffer.get(i+3)); + dstBuffer.put(i+1, srcBuffer.get(i+2)); + dstBuffer.put(i+2, srcBuffer.get(i+1)); + dstBuffer.put(i+3, srcBuffer.get(i)); + } + + dataIsByRef = false; + + } + } + } +} diff --git a/src/javax/media/j3d/ImageComponentUpdateInfo.java b/src/javax/media/j3d/ImageComponentUpdateInfo.java new file mode 100644 index 0000000..eeb45ad --- /dev/null +++ b/src/javax/media/j3d/ImageComponentUpdateInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/IndexedGeometryArray.java b/src/javax/media/j3d/IndexedGeometryArray.java new file mode 100644 index 0000000..113bd4f --- /dev/null +++ b/src/javax/media/j3d/IndexedGeometryArray.java @@ -0,0 +1,1278 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The IndexedGeometryArray object contains separate integer arrays + * that index into the arrays of positional coordinates, colors, + * normals, texture coordinates, and vertex attributes. + * 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() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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; + + /** + * Specifies that this IndexedGeometryArray allows reading the array of + * vertex attribute indices. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_VERTEX_ATTR_INDEX_READ = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_READ; + + /** + * Specifies that this IndexedGeometryArray allows writing the array of + * vertex attribute indices. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_VERTEX_ATTR_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_WRITE; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_COLOR_INDEX_READ, + ALLOW_COORDINATE_INDEX_READ, + ALLOW_NORMAL_INDEX_READ, + ALLOW_TEXCOORD_INDEX_READ, + ALLOW_VERTEX_ATTR_INDEX_READ + }; + + /** + * 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: + * + *

    + * validIndexCount : indexCount
    + * initialIndexIndex : 0
    + * all index array values : 0
    + *
+ * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount the number of indices in this object. This + * count is the maximum number of vertices that will be rendered. + * + * @exception IllegalArgumentException if indexCount < 0 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + public IndexedGeometryArray(int vertexCount, + int vertexFormat, + int indexCount) { + super(vertexCount, vertexFormat); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount the number of indices in this object. This + * count is the maximum number of vertices that will be rendered. + * + * @exception IllegalArgumentException if indexCount < 0 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.2 + */ + public IndexedGeometryArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int indexCount) { + this(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, 0, null, indexCount); + } + + /** + * Constructs an empty IndexedGeometryArray object with the + * specified number of vertices, vertex format, number of texture + * coordinate sets, texture coordinate mapping array, vertex + * attribute count, vertex attribute sizes array, and number of + * indices. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount the number of indices in this object. This + * count is the maximum number of vertices that will be rendered. + * + * @exception IllegalArgumentException if indexCount < 0 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedGeometryArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 indexCount. + * + * @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: + *
    + * validIndexCount < 0, or
    + * initialIndexIndex + validIndexCount > indexCount
    + *
+ * + * @exception ArrayIndexOutOfBoundsException if any element in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception ArrayIndexOutOfBoundsException if the data mode for this geometry + * array object is BY_REFERENCE_INDICES and + * coordIndices.length < (initialIndexIndex + validIndexCount). + * + * @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: + *
    + * initialIndexIndex < 0, or
    + * initialIndexIndex + validIndexCount > indexCount
    + *
+ * + * @exception ArrayIndexOutOfBoundsException if any element in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception ArrayIndexOutOfBoundsException if the data mode for this geometry + * array object is BY_REFERENCE_INDICES and + * coordIndices.length < (initialIndexIndex + validIndexCount). + * + * @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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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. + * + * @exception UnsupportedOperationException this method is not supported + * + * @since Java 3D 1.4 + */ + @Override + public void setInitialVertexAttrIndex(int vertexAttrNum, + int initialVertexAttrIndex) { + 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 + */ + @Override + public void setValidVertexCount(int validVertexCount) { + throw new UnsupportedOperationException(); + } + + + //NVaidya + /** + * 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE_INDICES. + */ + public void setCoordinateIndex(int index, int coordinateIndex) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray1")); + + //NVaidya + int format = ((IndexedGeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE_INDICES) != 0) + throw new IllegalStateException(J3dI18N.getString("IndexedGeometryArray31")); + + ((IndexedGeometryArrayRetained)this.retained).setCoordinateIndex(index, coordinateIndex); + } + + + //NVaidya + /** + * 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE_INDICES. + */ + public void setCoordinateIndices(int index, int coordinateIndices[]) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray1")); + + //NVaidya + int format = ((IndexedGeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE_INDICES) != 0) + throw new IllegalStateException(J3dI18N.getString("IndexedGeometryArray31")); + + ((IndexedGeometryArrayRetained)this.retained).setCoordinateIndices(index, coordinateIndices); + } + + //NVaidya + /** + * Sets the coordinate indices array reference to the specified array. + * If the coordinate indices array 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 coordIndices an array of indices to which a reference + * will be set. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalStateException if the data mode for this geometry + * array object is not BY_REFERENCE_INDICES. + * + * @exception ArrayIndexOutOfBoundsException if any element of the + * coordIndices array whose destination position is in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception ArrayIndexOutOfBoundsException if + * coordIndices.length < (initialIndexIndex + validIndexCount). + * + * @since Java 3D 1.5 + */ + public void setCoordIndicesRef(int coordIndices[]) { + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_REF_DATA_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86")); + + //NVaidya + int format = ((IndexedGeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE_INDICES) == 0) + throw new IllegalStateException(J3dI18N.getString("IndexedGeometryArray32")); + + ((IndexedGeometryArrayRetained)this.retained).setCoordIndicesRef(coordIndices); + } + + /** + * 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * setTextureCoordinateIndex(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception ArrayIndexOutOfBoundsException if index is in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @since Java 3D 1.2 + */ + public void setTextureCoordinateIndex(int texCoordSet, + int index, + int texCoordIndex) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_TEXCOORD_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 + * setTextureCoordinateIndices(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat 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 + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @since Java 3D 1.2 + */ + public void setTextureCoordinateIndices(int texCoordSet, + int index, + int texCoordIndices[]) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_TEXCOORD_INDEX_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray7")); + + ((IndexedGeometryArrayRetained)this.retained).setTextureCoordinateIndices(texCoordSet, index, texCoordIndices); + } + + /** + * Sets the vertex attribute index associated with the vertex at + * the specified index for the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index the vertex index + * @param vertexAttrIndex the new vertex attribute index + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception ArrayIndexOutOfBoundsException if index is in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * and the specified vertexAttrIndex is out of range. The + * vertexAttrIndex 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 vertex attribute array. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrIndex(int vertexAttrNum, + int index, + int vertexAttrIndex) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray28")); + } + } + + ((IndexedGeometryArrayRetained)this.retained).setVertexAttrIndex(vertexAttrNum, index, vertexAttrIndex); + } + + /** + * Sets the vertex attribute indices associated with the vertices + * starting at the specified index for the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index the vertex index + * @param vertexAttrIndices an array of vertex attribute indices + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception ArrayIndexOutOfBoundsException if any element of the + * vertexAttrIndices array whose destination position is in the range + * [initialIndexIndex, initialIndexIndex+validIndexCount-1] + * 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 vertex attribute array. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @since Java 3D 1.4 + */ + public void setVertexAttrIndices(int vertexAttrNum, + int index, + int[] vertexAttrIndices) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray28")); + } + } + + ((IndexedGeometryArrayRetained)this.retained).setVertexAttrIndices(vertexAttrNum, index, vertexAttrIndices); + } + + //NVaidya + /** + * 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 + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE_INDICES. + */ + public int getCoordinateIndex(int index) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_COORDINATE_INDEX_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray9")); + + //NVaidya + int format = ((IndexedGeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE_INDICES) != 0) + throw new IllegalStateException(J3dI18N.getString("IndexedGeometryArray31")); + + return ((IndexedGeometryArrayRetained)this.retained).getCoordinateIndex(index); + } + + //NVaidya + /** + * 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 + * + * @exception IllegalStateException if the data mode for this geometry + * array object is BY_REFERENCE_INDICES. + */ + public void getCoordinateIndices(int index, int coordinateIndices[]) { + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_COORDINATE_INDEX_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray9")); + + //NVaidya + int format = ((IndexedGeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE_INDICES) != 0) + throw new IllegalStateException(J3dI18N.getString("IndexedGeometryArray31")); + + ((IndexedGeometryArrayRetained)this.retained).getCoordinateIndices(index, coordinateIndices); + } + + //NVaidya + /** + * Returns a reference to the coordinate indices associated with + * the vertices + * @return the coordinate indices array + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalStateException if the data mode for this geometry + * array object is not BY_REFERENCE_INDICES. + * + * @since Java 3D 1.5 + */ + public int[] getCoordIndicesRef() { + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_REF_DATA_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87")); + + int format = ((IndexedGeometryArrayRetained)this.retained).vertexFormat; + if ((format & BY_REFERENCE_INDICES) == 0) + throw new IllegalStateException(J3dI18N.getString("IndexedGeometryArray32")); + + return ((IndexedGeometryArrayRetained)this.retained).getCoordIndicesRef(); + } + + /** + * 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 + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + */ + 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 + * getTextureCoordinateIndex(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @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 + * getTextureCoordinateIndices(int texCoordSet, ...) + */ + 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 + * TEXTURE_COORDINATE bits are set in the + * vertexFormat or if the index or + * texCoordSet is out of range. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @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); + } + + /** + * Retrieves the vertex attribute index associated with the vertex at + * the specified index for the specified vertex attribute number + * for this object. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index the vertex index + * + * @return the vertex attribute index + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @since Java 3D 1.4 + */ + public int getVertexAttrIndex(int vertexAttrNum, + int index) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray29")); + } + } + + return ((IndexedGeometryArrayRetained)this.retained).getVertexAttrIndex(vertexAttrNum, index); + } + + /** + * Retrieves the vertex attribute indices associated with the vertices + * starting at the specified index for the specified vertex attribute number + * for this object. The vertex attribute indices + * are copied into the specified array. The array + * must be large enough to hold all of the indices. + * + * @param vertexAttrNum vertex attribute number in this geometry array + * @param index the vertex index + * @param vertexAttrIndices array that will receive the vertex attribute indices + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception ArrayIndexOutOfBoundsException if the index or + * vertexAttrNum is out of range. + * + * @exception NullPointerException if the USE_COORD_INDEX_ONLY + * bit is set in vertexFormat. + * + * @since Java 3D 1.4 + */ + public void getVertexAttrIndices(int vertexAttrNum, + int index, + int[] vertexAttrIndices) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray29")); + } + } + + ((IndexedGeometryArrayRetained)this.retained).getVertexAttrIndices(vertexAttrNum, index, vertexAttrIndices); + } + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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 & COORDINATES) != 0) { + ga.getCoordinateIndices(0, buffer); + rt.setCoordinateIndices(0, buffer); + } + + if ((vformat & USE_COORD_INDEX_ONLY) == 0) { + if ((vformat & NORMALS) != 0) { + ga.getNormalIndices(0, buffer); + rt.setNormalIndices(0, buffer); + } + + if ((vformat & COLOR) != 0) { + ga.getColorIndices(0, buffer); + rt.setColorIndices(0, buffer); + } + + if ((vformat & VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < ga.vertexAttrCount; i++) { + ga.getVertexAttrIndices(i, 0, buffer); + rt.setVertexAttrIndices(i, 0, buffer); + } + } + + if ((vformat & 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/javax/media/j3d/IndexedGeometryArrayRetained.java b/src/javax/media/j3d/IndexedGeometryArrayRetained.java new file mode 100644 index 0000000..528c46f --- /dev/null +++ b/src/javax/media/j3d/IndexedGeometryArrayRetained.java @@ -0,0 +1,1932 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.util.ArrayList; + +import javax.vecmath.TexCoord2f; +import javax.vecmath.TexCoord3f; + +/** + * 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, vertexAttr + int[] indexCoord; + int[] indexColor; + int[] indexNormal; + int[][] indexTexCoord; + int[][] indexVertexAttr; + + int indexCount = 0; + + 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; + int[] maxVertexAttrIndices = null; + + void createIndexedGeometryArrayData(int indexCount) { + this.indexCount = indexCount; + this.validIndexCount = indexCount; + + // Only allocate color, normal, texCoord, and vertexAttr + // index arrays if USE_COORD_INDEX_ONLY is not set + boolean notUCIO = (this.vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0; + + //NVaidya + // Only allocate indexCoord if BY_REFERENCE_INDICES not set + if(((this.vertexFormat & GeometryArray.COORDINATES) != 0) && + ((this.vertexFormat & GeometryArray.BY_REFERENCE_INDICES) == 0)) + this.indexCoord = new int[indexCount]; + + if(((this.vertexFormat & GeometryArray.NORMALS) != 0) && notUCIO) + this.indexNormal = new int[indexCount]; + + if(((this.vertexFormat & GeometryArray.COLOR) != 0) && notUCIO) + this.indexColor = new int[indexCount]; + + if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) { + this.indexTexCoord = new int[this.texCoordSetCount][]; + if(notUCIO) { + for (int i = 0; i < this.texCoordSetCount; i++) { + this.indexTexCoord[i] = new int[indexCount]; + } + } + maxTexCoordIndices = new int[texCoordSetCount]; + } + + if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + this.indexVertexAttr = new int[this.vertexAttrCount][]; + if (notUCIO) { + for (int i = 0; i < this.vertexAttrCount; i++) { + this.indexVertexAttr[i] = new int[indexCount]; + } + } + this.maxVertexAttrIndices = new int[this.vertexAttrCount]; + } + } + + + GeometryArrayRetained cloneNonIndexedGeometry() { + GeometryArrayRetained obj = null; + + 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; + default: + assert false; // Should never get here + } + + obj.createGeometryArrayData(validIndexCount, + (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)), + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + obj.cloneSourceArray = this; + obj.unIndexify(this); + obj.source=source; + + return obj; + } + + + /** + * 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.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + doVertexAttrCheck(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: + FloatBuffer texBuffer = (FloatBuffer)refTexCoordsBuffer[texCoordSet].getROBuffer(); + 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")); + } + } + } + } + + } + + void doVertexAttrCheck(int newMax, int vertexAttrNum) { + + // Check to make sure that the array length defined by the user is ateast maxVertexAttrIndex long + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) == 0) { + return; + } + + // Vertex attributes must not be interleaved + assert (vertexFormat & GeometryArray.INTERLEAVED) == 0; + + if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) { + if (newMax >= vertexCount) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30")); + } + } else { + int multiplier = vertexAttrSizes[vertexAttrNum]; + + if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) { + switch (vertexType & VATTR_DEFINED) { + case AF: + if(multiplier * newMax >= floatBufferRefVertexAttrs[vertexAttrNum].limit()) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30")); + } + break; + } + } else { + switch (vertexType & VATTR_DEFINED) { + case AF: + if (multiplier * newMax >= floatRefVertexAttrs[vertexAttrNum].length) { + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30")); + } + break; + } + } + } + } + + + /** + * 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.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newMax; + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + maxNormalIndex = newMax; + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= INDEX_CHANGED; + this.indexCoord[index] = coordinateIndex; + maxCoordIndex = newMax; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && 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.VERTEX_ATTRIBUTES) != 0) { + for (i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newMax; + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + maxNormalIndex = newMax; + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= INDEX_CHANGED; + maxCoordIndex = newMax; + for (i=0, j = index; i < num;i++, j++) { + this.indexCoord[j] = coordinateIndices[i]; + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(true); + } + } + + //NVaidya + /** + * Sets the coordinate indices by reference to the specified array + * @param coordinateIndices an array of coordinate indices + */ + final void setCoordIndicesRef(int coordinateIndices[]) { + int newMax = 0; + + if (coordinateIndices != null) { + if (coordinateIndices.length < initialIndexIndex + validIndexCount) { + throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray33")); + } + + // + // option 1: could fake the args to "re-use" doIndicesCheck() + //NVaidya + // newMax = doIndicesCheck(0, maxCoordIndex, coordinateIndices, coordinateIndices); + // if (newMax > maxCoordIndex) { + // doErrorCheck(newMax); + // } + // + // option 2: same logic as in setInitialIndexIndex: Better, I Think ? + // computeMaxIndex() doesn't check for index < 0 while doIndicesCheck() does. + // So, a new method computeMaxIndexWithCheck + //NVaidya + newMax = computeMaxIndexWithCheck(initialIndexIndex, validIndexCount, 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 (int i = 0; i < texCoordSetCount; i++) { + maxTexCoordIndices[i] = newMax; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newMax; + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + maxNormalIndex = newMax; + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + dirtyFlag |= INDEX_CHANGED; + maxCoordIndex = newMax; + this.indexCoord = coordinateIndices; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(true); + } + } + + //NVaidya + /** + * trigger from GeometryArrayRetained#updateData() + * to recompute maxCoordIndex and perform index integrity checks + */ + final void doPostUpdaterUpdate() { + // user may have called setCoordIndicesRef and/or + // changed contents of indexCoord array. Thus, need to + // recompute maxCoordIndex unconditionally (and redundantly + // if user had only invoked setCoordIndicesRef but not also + // changed contents). geomLock is currently locked. + + // Option 1: + // simply call setCoordIndicesRef(indexCoord); but this seems to cause + // deadlock or freeze - probably because the !inUpdater branch sends + // out too many sendDataChangedMessage(true) - occurs if updateData + // method is called rapidly. + // setCoordIndicesRef(indexCoord); + + // Option 2: + // use only necessary code from setCoordIndicesRef + // System.err.println("IndexedGeometryArrayretained#doUpdaterUpdate"); + int newMax = 0; + + if (indexCoord != null) { + newMax = computeMaxIndexWithCheck(initialIndexIndex, validIndexCount, indexCoord); + 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.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newMax; + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + maxNormalIndex = newMax; + } + } + + dirtyFlag |= INDEX_CHANGED; + maxCoordIndex = newMax; + } + + /** + * 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; + + newMax = doIndexCheck(index, maxColorIndex, indexColor, colorIndex); + if (newMax > maxColorIndex) { + doColorCheck(newMax); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * 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; + + newMax = doIndicesCheck(index, maxColorIndex, indexColor, colorIndices); + if (newMax > maxColorIndex) { + doColorCheck(newMax); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + maxColorIndex = newMax; + for (i=0, j = index; i < num;i++, j++) { + this.indexColor[j] = colorIndices[i]; + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * 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; + + newMax = doIndexCheck(index, maxNormalIndex, indexNormal, normalIndex); + if (newMax > maxNormalIndex) { + doNormalCheck(newMax); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + maxNormalIndex = newMax; + this.indexNormal[index] = normalIndex; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * 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; + + newMax = doIndicesCheck(index, maxNormalIndex, indexNormal, normalIndices); + if (newMax > maxNormalIndex) { + doNormalCheck(newMax); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + for (i=0, j = index; i < num;i++, j++) { + this.indexNormal[j] = normalIndices[i]; + } + maxNormalIndex = newMax; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * 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 = this.indexTexCoord[texCoordSet]; + + newMax = doIndexCheck(index, maxTexCoordIndices[texCoordSet],indices, texCoordIndex); + if (newMax > maxTexCoordIndices[texCoordSet]) { + doTexCoordCheck(newMax, texCoordSet); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + maxTexCoordIndices[texCoordSet] = newMax; + indices[index] = texCoordIndex; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * 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 = this.indexTexCoord[texCoordSet]; + + int newMax; + + newMax = doIndicesCheck(index, maxTexCoordIndices[texCoordSet], indices, texCoordIndices); + if (newMax > maxTexCoordIndices[texCoordSet]) { + doTexCoordCheck(newMax, texCoordSet); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + maxTexCoordIndices[texCoordSet] = newMax; + for (i=0, j = index; i < num;i++, j++) { + indices[j] = texCoordIndices[i]; + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attribute index associated with the vertex at + * the specified index for the specified vertex attribute number + * for this object. + */ + void setVertexAttrIndex(int vertexAttrNum, + int index, + int vertexAttrIndex) { + + int newMax; + int [] indices = this.indexVertexAttr[vertexAttrNum]; + + newMax = doIndexCheck(index, maxVertexAttrIndices[vertexAttrNum],indices, vertexAttrIndex); + if (newMax > maxVertexAttrIndices[vertexAttrNum]) { + doVertexAttrCheck(newMax, vertexAttrNum); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + maxVertexAttrIndices[vertexAttrNum] = newMax; + indices[index] = vertexAttrIndex; + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * Sets the vertex attribute indices associated with the vertices + * starting at the specified index for the specified vertex attribute number + * for this object. + */ + void setVertexAttrIndices(int vertexAttrNum, + int index, + int[] vertexAttrIndices) { + + int i, j, num = vertexAttrIndices.length; + int [] indices = this.indexVertexAttr[vertexAttrNum]; + + int newMax; + + newMax = doIndicesCheck(index, maxVertexAttrIndices[vertexAttrNum], indices, vertexAttrIndices); + if (newMax > maxVertexAttrIndices[vertexAttrNum]) { + doVertexAttrCheck(newMax, vertexAttrNum); + } + boolean isLive = source!=null && source.isLive(); + if(isLive){ + geomLock.getLock(); + } + maxVertexAttrIndices[vertexAttrNum] = newMax; + for (i=0, j = index; i < num;i++, j++) { + indices[j] = vertexAttrIndices[i]; + } + if(isLive) { + geomLock.unLock(); + } + if (!inUpdater && isLive) { + sendDataChangedMessage(false); + } + } + + /** + * 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]; + } + } + + //NVaidya + /** + * Returns a reference to the coordinate indices associated + * with the vertices + */ + final int[] getCoordIndicesRef() { + return this.indexCoord; + } + + /** + * 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) { + int [] indices = this.indexTexCoord[texCoordSet]; + + return indices[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 = this.indexTexCoord[texCoordSet]; + + for (i=0, j = index;i < num;i++, j++) { + texCoordIndices[i] = indices[j]; + } + } + + /** + * Retrieves the vertex attribute index associated with the vertex at + * the specified index for the specified vertex attribute number + * for this object. + */ + int getVertexAttrIndex(int vertexAttrNum, + int index) { + + int [] indices = this.indexVertexAttr[vertexAttrNum]; + + return indices[index]; + } + + /** + * Retrieves the vertex attribute indices associated with the vertices + * starting at the specified index for the specified vertex attribute number + * for this object. + */ + void getVertexAttrIndices(int vertexAttrNum, + int index, + int[] vertexAttrIndices) { + + int i, j, num = vertexAttrIndices.length; + int [] indices = this.indexVertexAttr[vertexAttrNum]; + + for (i=0, j = index;i < num;i++, j++) { + vertexAttrIndices[i] = indices[j]; + } + } + + + @Override + void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, + boolean updateAlpha, float alpha, + int screen, boolean ignoreVertexColors) { + + int cdirty; + boolean useAlpha = false; + Object[] retVal; + if (mirrorGeometry != null) { + mirrorGeometry.execute(cv, ra, isNonUniformScale, updateAlpha, alpha, + screen, ignoreVertexColors); + return; + } + + // Check if index array is null; if yes, don't draw anything + if (indexCoord == null) { + return; + } + + //By reference with java array + if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) { + if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) { + float[] vdata; + // System.err.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; + } + + Pipeline.getPipeline().executeIndexedGeometry(cv.ctx, + this, geoType, isNonUniformScale, + useAlpha, + ignoreVertexColors, + initialIndexIndex, + validIndexCount, + // Vertex Count is maxCoordIndex + 1 + maxCoordIndex + 1, + ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat, + vertexAttrCount, vertexAttrSizes, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + cv.numActiveTexUnit, + vdata, null, + 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; + } + + Pipeline.getPipeline().executeIndexedGeometry(cv.ctx, + this, geoType, isNonUniformScale, + useAlpha, + ignoreVertexColors, + initialIndexIndex, + validIndexCount, + maxCoordIndex + 1, + vertexFormat, + vertexAttrCount, vertexAttrSizes, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + cv.numActiveTexUnit, + interLeavedVertexData, cdata, + 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.VERTEX_ATTRIBUTES) != 0) && + (vertexType & VATTR_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 & VATTR_DEFINED) != 0) + vdefined |= VATTR_FLOAT; + if((vertexType & TEXCOORD_DEFINED) != 0) + vdefined |= TEXCOORD_FLOAT; + + Pipeline.getPipeline().executeIndexedGeometryVA(cv.ctx, + this, geoType, isNonUniformScale, + ignoreVertexColors, + initialIndexIndex, + validIndexCount, + maxCoordIndex + 1, + (vertexFormat | c4fAllocated), + vdefined, + mirrorFloatRefCoords, mirrorDoubleRefCoords, + cfdata, cbdata, + mirrorFloatRefNormals, + vertexAttrCount, vertexAttrSizes, + mirrorFloatRefVertexAttrs, + ((texCoordSetMap == null) ? 0:texCoordSetMap.length), + texCoordSetMap, + cv.numActiveTexUnit, + 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; + } + + Pipeline.getPipeline().executeIndexedGeometryBuffer(cv.ctx, + this, geoType, isNonUniformScale, + useAlpha, + ignoreVertexColors, + initialIndexIndex, + validIndexCount, + maxCoordIndex + 1, + vertexFormat, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + cv.numActiveTexUnit, + interleavedFloatBufferImpl, cdata, + 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.VERTEX_ATTRIBUTES) != 0) && + (vertexType & VATTR_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 { + // XXXX: 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 { + // XXXX: 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; + } + + Buffer vcoord = null; + Buffer cdataBuffer = null; + FloatBuffer normal = null; + + int vdefined = 0; + if((vertexType & PF) != 0) { + vdefined |= COORD_FLOAT; + vcoord = floatBufferRefCoords; + } else if((vertexType & PD ) != 0) { + vdefined |= COORD_DOUBLE; + vcoord = doubleBufferRefCoords; + } + if((vertexType & CF ) != 0) { + vdefined |= COLOR_FLOAT; + cdataBuffer = floatBufferRefColors; + } else if((vertexType & CUB) != 0) { + vdefined |= COLOR_BYTE; + cdataBuffer = byteBufferRefColors; + } + + if((vertexType & NORMAL_DEFINED) != 0) { + vdefined |= NORMAL_FLOAT; + normal = floatBufferRefNormals; + } + + if ((vertexType & VATTR_DEFINED) != 0) { + vdefined |= VATTR_FLOAT; + } + + if ((vertexType & TEXCOORD_DEFINED) != 0) { + vdefined |= TEXCOORD_FLOAT; + } + + Pipeline.getPipeline().executeIndexedGeometryVABuffer(cv.ctx, + this, geoType, isNonUniformScale, + ignoreVertexColors, + initialIndexIndex, + validIndexCount, + maxCoordIndex + 1, + (vertexFormat | c4fAllocated), + vdefined, + vcoord, + cdataBuffer, + cfdata, cbdata, + normal, + vertexAttrCount, vertexAttrSizes, + floatBufferRefVertexAttrs, + ((texCoordSetMap == null) ? 0:texCoordSetMap.length), + texCoordSetMap, + cv.numActiveTexUnit, + texCoordStride, + refTexCoords, cdirty, indexCoord); + + } + } // end of non interleaved and by reference + } // end of nio buffer + } + + @Override + 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.err.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; + } + + Pipeline.getPipeline().buildIndexedGeometry(cv.ctx, + this, geoType, isNonUniformScale, + updateAlpha, alpha, ignoreVertexColors, + initialIndexIndex, + validIndexCount, + maxCoordIndex + 1, + vertexFormat, + vertexAttrCount, vertexAttrSizes, + texCoordSetCount, texCoordSetMap, + (texCoordSetMap == null) ? 0 : texCoordSetMap.length, + texCoordSetMapOffset, + (xform == null) ? null : xform.mat, + (nxform == null) ? null : nxform.mat, + vdata, indexCoord); + } + // XXXX: Note that there is no "else" clause here, and no + // buildIndexedGeometryForByRef() method. + // We would need to create one if we ever wanted to support by-ref + // indexed geometry in display lists. Better yet, we could fix + // canBeInDisplayList so that unindexified by-ref geometry could + // go into a display list. + } + } + + @Override + 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]; + boolean notUCIO = (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0; + if (notUCIO) { + if ((vertexFormat & GeometryArray.COLOR) != 0) + indexColor = new int[indexCount]; + if ((vertexFormat & GeometryArray.NORMALS) != 0) + indexNormal = new int[indexCount]; + // We only merge if the texCoordSetCount is 1 and there are no + // vertex attrs + if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) { + indexTexCoord = new int[1][]; + indexTexCoord[0] = new int[indexCount]; + texCoord = 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 (notUCIO) { + 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] = 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); + } + + + @Override + 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_VERTEX_ATTR_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; + if (indices != null) { + for (int i = initial; i < (initial+count); i++) { + if (indices[i] > maxIndex) { + maxIndex = indices[i]; + } + } + } + return maxIndex; + + } + + //NVaidya + // same as computeMaxIndex method but checks for index < 0 + int computeMaxIndexWithCheck(int initial, int count, int[] indices) { + int maxIndex = 0; + for (int i = initial; i < (initial+count); i++) { + // Throw an exception, since index is negative + if (indices[i] < 0) + throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray27")); + 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")); + } + if ((vertexFormat & GeometryArray.BY_REFERENCE_INDICES) != 0) { + if (indexCoord != null && indexCoord.length < initialIndexIndex + validIndexCount) { + throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray33")); + } + } + int newCoordMax =0; + int newColorIndex=0; + int newNormalIndex=0; + int[] newTexCoordIndex = null; + int[] newVertexAttrIndex = 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, + indexTexCoord[i]); + doTexCoordCheck(newTexCoordIndex[i], i); + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + newVertexAttrIndex = new int[vertexAttrCount]; + for (int i = 0; i < vertexAttrCount; i++) { + newVertexAttrIndex[i] = computeMaxIndex(initialIndexIndex, + validIndexCount, + indexVertexAttr[i]); + doVertexAttrCheck(newVertexAttrIndex[i], i); + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + newNormalIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexNormal); + doNormalCheck(newNormalIndex); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newVertexAttrIndex[i]; + } + } + maxNormalIndex = newNormalIndex; + } + else { + maxColorIndex = maxCoordIndex; + maxNormalIndex = maxCoordIndex; + if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) { + for (int i = 0; i < texCoordSetCount; i++) { + maxTexCoordIndices[i] = maxCoordIndex; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = maxCoordIndex; + } + } + } + if(isLive) { + geomLock.unLock(); + } + // bbox is computed for the entries list. + // so, send as false + if (!inUpdater && isLive) { + sendDataChangedMessage(true); + } + + } + + void setInitialIndexIndex(int initialIndexIndex) { + if ((initialIndexIndex + validIndexCount) > indexCount) { + throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray22")); + } + if ((vertexFormat & GeometryArray.BY_REFERENCE_INDICES) != 0) { + if (indexCoord != null && indexCoord.length < initialIndexIndex + validIndexCount) { + throw new IllegalArgumentException(J3dI18N.getString("IndexedGeometryArray33")); + } + } + + int newCoordMax =0; + int newColorIndex=0; + int newNormalIndex=0; + int[] newTexCoordIndex = null; + int[] newVertexAttrIndex = 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, + indexTexCoord[i]); + doTexCoordCheck(newTexCoordIndex[i], i); + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + newVertexAttrIndex = new int[vertexAttrCount]; + for (int i = 0; i < vertexAttrCount; i++) { + newVertexAttrIndex[i] = computeMaxIndex(initialIndexIndex, + validIndexCount, + indexVertexAttr[i]); + doVertexAttrCheck(newVertexAttrIndex[i], i); + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + newNormalIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexNormal); + doNormalCheck(newNormalIndex); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newVertexAttrIndex[i]; + } + } + maxNormalIndex = newNormalIndex; + } + else { + maxColorIndex = maxCoordIndex; + maxNormalIndex = maxCoordIndex; + if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) { + for (int i = 0; i < texCoordSetCount; i++) { + maxTexCoordIndices[i] = maxCoordIndex; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = maxCoordIndex; + } + } + } + if(isLive) { + geomLock.unLock(); + } + // bbox is computed for the entries list. + // so, send as false + if (!inUpdater && isLive) { + sendDataChangedMessage(true); + } + } + + int getInitialIndexIndex() { + return initialIndexIndex; + } + + int getValidIndexCount() { + return validIndexCount; + } + + @Override + 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.VERTEX_ATTRIBUTES) != 0)&& + bit == IndexedGeometryArray.ALLOW_VERTEX_ATTR_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/javax/media/j3d/IndexedGeometryStripArray.java b/src/javax/media/j3d/IndexedGeometryStripArray.java new file mode 100644 index 0000000..7253aac --- /dev/null +++ b/src/javax/media/j3d/IndexedGeometryStripArray.java @@ -0,0 +1,261 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @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 + * validIndexCount > indexCount + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @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 + * validIndexCount > indexCount + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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); + } + + /** + * Constructs an empty IndexedGeometryStripArray object with the + * specified number of vertices, vertex format, number of texture + * coordinate sets, texture coordinate mapping array, vertex + * attribute count, vertex attribute sizes array, number of + * indices, and array of per-strip index counts. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @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 + * validIndexCount > indexCount + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedGeometryStripArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount, + int[] stripIndexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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 + * initialIndexIndex + validIndexCount > indexCount + * + * @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 + */ + @Override + public void setValidIndexCount(int validIndexCount) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/javax/media/j3d/IndexedGeometryStripArrayRetained.java b/src/javax/media/j3d/IndexedGeometryStripArrayRetained.java new file mode 100644 index 0000000..d3e874f --- /dev/null +++ b/src/javax/media/j3d/IndexedGeometryStripArrayRetained.java @@ -0,0 +1,247 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +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; + int[] newVertexAttrIndex = 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, + indexTexCoord[i]); + doTexCoordCheck(newTexCoordIndex[i], i); + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + newVertexAttrIndex = new int[vertexAttrCount]; + for (i = 0; i < vertexAttrCount; i++) { + newVertexAttrIndex[i] = computeMaxIndex(initialIndexIndex, + total, + indexVertexAttr[i]); + doTexCoordCheck(newVertexAttrIndex[i], i); + } + } + if ((vertexFormat & GeometryArray.NORMALS) != 0) { + newNormalIndex = computeMaxIndex(initialIndexIndex, total, indexNormal); + doNormalCheck(newNormalIndex); + } + } + + boolean isLive = source!=null && source.isLive(); + if(isLive){ + 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]; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = newVertexAttrIndex[i]; + } + } + maxNormalIndex = newNormalIndex; + } + else { + maxColorIndex = maxCoordIndex; + maxNormalIndex = maxCoordIndex; + if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) { + for (i = 0; i < texCoordSetCount; i++) { + maxTexCoordIndices[i] = maxCoordIndex; + } + } + if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (i = 0; i < vertexAttrCount; i++) { + maxVertexAttrIndices[i] = maxCoordIndex; + } + } + } + if(isLive) { + geomLock.unLock(); + } + // bbox is computed for the entries list. + // so, send as false + if (!inUpdater && isLive) { + sendDataChangedMessage(true); + } + + } + + @Override + GeometryArrayRetained cloneNonIndexedGeometry() { + GeometryStripArrayRetained obj = null; + + 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, + vertexAttrCount, vertexAttrSizes); + obj.unIndexify(this); + obj.setStripVertexCounts(stripIndexCounts); + obj.cloneSourceArray = this; + obj.source = source; + + return 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]; + } + } + + @Override + 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/javax/media/j3d/IndexedLineArray.java b/src/javax/media/j3d/IndexedLineArray.java new file mode 100644 index 0000000..f010506 --- /dev/null +++ b/src/javax/media/j3d/IndexedLineArray.java @@ -0,0 +1,219 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 2, or indexCount is not + * a multiple of 2 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 2, or indexCount is not + * a multiple of 2 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedLineArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 2, or indexCount is not + * a multiple of 2 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedLineArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedLineArrayRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedLineArrayRetained rt = (IndexedLineArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedLineArray l = new IndexedLineArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount()); + l.duplicateNodeComponent(this); + return l; + } +} diff --git a/src/javax/media/j3d/IndexedLineArrayRetained.java b/src/javax/media/j3d/IndexedLineArrayRetained.java new file mode 100644 index 0000000..b28b830 --- /dev/null +++ b/src/javax/media/j3d/IndexedLineArrayRetained.java @@ -0,0 +1,431 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + Point3d pnts[] = new Point3d[2]; + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + //NVaidya + // Bug 447: While loops below now traverse over all + // elements in the valid index range from initialIndexIndex + // to initialIndexInex + validIndexCount - 1 + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + pnts[0] = new Point3d(); + pnts[1] = new Point3d(); + int[] vtxIndexArr = new int[2]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin, + pickRay.direction, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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 < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectLineAndRay(pnts[0], pnts[1], + pickSegment.start, + dir, sdist, iPnt) && + (sdist[0] <= 1.0)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < loopStopIndex) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[k]); + } + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + + } + + @Override + boolean intersect(Point3d[] pnts) { + Point3d[] points = new Point3d[2]; + Vector3d dir; + double dist[] = new double[1]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + points[0] = new Point3d(); + points[1] = new Point3d(); + + switch (pnts.length) { + case 3: // Triangle/Quad , common case first + case 4: + while (i < loopStopIndex) { + 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 < loopStopIndex) { + 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 < loopStopIndex) { + 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; + } + + + @Override + boolean intersect(Transform3D thisToOtherVworld, + GeometryRetained geom) { + + Point3d[] pnts = new Point3d[2]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + pnts[0] = new Point3d(); + pnts[1] = new Point3d(); + + while (i < loopStopIndex) { + 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 + @Override + boolean intersect(Bounds targetBound) { + Point3d[] pnts = new Point3d[2]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + pnts[0] = new Point3d(); + pnts[1] = new Point3d(); + + switch(targetBound.getPickType()) { + case PickShape.PICKBOUNDINGBOX: + BoundingBox box = (BoundingBox) targetBound; + + while(i < loopStopIndex) { + 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 < loopStopIndex) { + 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 < loopStopIndex) { + 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; + } + + @Override + int getClassType() { + return LINE_TYPE; + } +} diff --git a/src/javax/media/j3d/IndexedLineStripArray.java b/src/javax/media/j3d/IndexedLineStripArray.java new file mode 100644 index 0000000..e956d23 --- /dev/null +++ b/src/javax/media/j3d/IndexedLineStripArray.java @@ -0,0 +1,247 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])} + * for more exceptions that can be thrown + */ + 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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedLineStripArray object using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedLineStripArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount, + int[] stripIndexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedLineStripArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedLineStripArrayRetained rt = + (IndexedLineStripArrayRetained) retained; + int stripIndexCounts[] = new int[rt.getNumStrips()]; + rt.getStripIndexCounts(stripIndexCounts); + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedLineStripArray l = new IndexedLineStripArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount(), + stripIndexCounts); + l.duplicateNodeComponent(this); + return l; + } +} diff --git a/src/javax/media/j3d/IndexedLineStripArrayRetained.java b/src/javax/media/j3d/IndexedLineStripArrayRetained.java new file mode 100644 index 0000000..21b5205 --- /dev/null +++ b/src/javax/media/j3d/IndexedLineStripArrayRetained.java @@ -0,0 +1,494 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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; + int[] vtxIndexArr = new int[2]; + + pnts[0] = new Point3d(); + pnts[1] = new Point3d(); + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < stripIndexCounts.length) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin, + pickRay.direction, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[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) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectLineAndRay(pnts[0], pnts[1], + pickSegment.start, + dir, sdist, iPnt) && + (sdist[0] <= 1.0)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < stripIndexCounts.length) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < stripIndexCounts.length) { + vtxIndexArr[0] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[0]); + scount = stripIndexCounts[i++]; + for (j=1; j < scount; j++) { + vtxIndexArr[1] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[1]); + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + // intersect pnts[] with every triangle in this object + @Override + 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; + } + + @Override + 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 + @Override + 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; + } + + @Override + int getClassType() { + return LINE_TYPE; + } +} diff --git a/src/javax/media/j3d/IndexedObject.java b/src/javax/media/j3d/IndexedObject.java new file mode 100644 index 0000000..a739769 --- /dev/null +++ b/src/javax/media/j3d/IndexedObject.java @@ -0,0 +1,68 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/IndexedPointArray.java b/src/javax/media/j3d/IndexedPointArray.java new file mode 100644 index 0000000..4373fb3 --- /dev/null +++ b/src/javax/media/j3d/IndexedPointArray.java @@ -0,0 +1,218 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1 + * or indexCount is less than 1 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1 + * or indexCount is less than 1 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedPointArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1 + * or indexCount is less than 1 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedPointArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedPointArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedPointArrayRetained rt = (IndexedPointArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedPointArray p = new IndexedPointArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount()); + p.duplicateNodeComponent(this); + return p; + } +} diff --git a/src/javax/media/j3d/IndexedPointArrayRetained.java b/src/javax/media/j3d/IndexedPointArrayRetained.java new file mode 100644 index 0000000..4d7970f --- /dev/null +++ b/src/javax/media/j3d/IndexedPointArrayRetained.java @@ -0,0 +1,309 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * The IndexedPointArray object draws the array of vertices as individual points. + */ + +class IndexedPointArrayRetained extends IndexedGeometryArrayRetained { + + IndexedPointArrayRetained() { + this.geoType = GEO_TYPE_INDEXED_POINT_SET; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + Point3d pnt = new Point3d(); + int[] vtxIndexArr = new int[1]; + + int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ? + initialVertexIndex : initialCoordIndex); + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < validVertexCount) { + vtxIndexArr[0] = indexCoord[i]; + getVertexData(indexCoord[i++], pnt); + if (intersectPntAndRay(pnt, pickRay.origin, + pickRay.direction, sdist)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + vtxIndexArr[0] = indexCoord[i]; + getVertexData(indexCoord[i++], pnt); + if (intersectPntAndRay(pnt, pickSegment.start, + dir, sdist) && + (sdist[0] <= 1.0)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + case PickShape.PICKBOUNDINGSPHERE: + case PickShape.PICKBOUNDINGPOLYTOPE: + Bounds bounds = ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + vtxIndexArr[0] = indexCoord[i]; + getVertexData(indexCoord[i++], pnt); + if (bounds.intersect(pnt)) { + if (flags == 0) { + return true; + } + sdist[0] = pickShape.distance(pnt); + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < validVertexCount) { + vtxIndexArr[0] = indexCoord[i]; + getVertexData(indexCoord[i++], pnt); + if (intersectCylinder(pnt, pickCylinder, sdist)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < validVertexCount) { + vtxIndexArr[0] = indexCoord[i]; + getVertexData(indexCoord[i++], pnt); + if (intersectCone(pnt, pickCone, sdist)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + @Override + 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; + } + + @Override + 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 + @Override + 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; + } + + @Override + int getClassType() { + return POINT_TYPE; + } +} + diff --git a/src/javax/media/j3d/IndexedQuadArray.java b/src/javax/media/j3d/IndexedQuadArray.java new file mode 100644 index 0000000..0ed40d1 --- /dev/null +++ b/src/javax/media/j3d/IndexedQuadArray.java @@ -0,0 +1,222 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 4, or indexCount is not + * a multiple of 4 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 4, or indexCount is not + * a multiple of 4 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedQuadArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 4, or indexCount is not + * a multiple of 4 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedQuadArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedQuadArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedQuadArrayRetained rt = (IndexedQuadArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedQuadArray q = new IndexedQuadArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount()); + q.duplicateNodeComponent(this); + return q; + } +} diff --git a/src/javax/media/j3d/IndexedQuadArrayRetained.java b/src/javax/media/j3d/IndexedQuadArrayRetained.java new file mode 100644 index 0000000..b32ac13 --- /dev/null +++ b/src/javax/media/j3d/IndexedQuadArrayRetained.java @@ -0,0 +1,459 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + Point3d pnts[] = new Point3d[4]; + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + int[] vtxIndexArr = new int[4]; + + //NVaidya + // Bug 447: While loops below now traverse over all + // elements in the valid index range from initialIndexIndex + // to initialIndexInex + validIndexCount - 1 + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + 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 < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + while (i < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + while (i < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + while (i < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + while (i < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + while (i < loopStopIndex) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + + } + + // intersect pnts[] with every quad in this object + @Override + boolean intersect(Point3d[] pnts) { + Point3d[] points = new Point3d[4]; + double dist[] = new double[1]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + + points[0] = new Point3d(); + points[1] = new Point3d(); + points[2] = new Point3d(); + points[3] = new Point3d(); + + switch (pnts.length) { + case 3: // Triangle + while (i < loopStopIndex) { + 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 < loopStopIndex) { + 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 < loopStopIndex) { + 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 < loopStopIndex) { + 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; + } + + + @Override + boolean intersect(Transform3D thisToOtherVworld, + GeometryRetained geom) { + + Point3d[] points = new Point3d[4]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + + points[0] = new Point3d(); + points[1] = new Point3d(); + points[2] = new Point3d(); + points[3] = new Point3d(); + + while (i < loopStopIndex) { + 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 + @Override + boolean intersect(Bounds targetBound) { + Point3d[] points = new Point3d[4]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + + 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 < loopStopIndex) { + 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 < loopStopIndex) { + 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 < loopStopIndex) { + 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; + } + + + @Override + int getClassType() { + return QUAD_TYPE; + } +} diff --git a/src/javax/media/j3d/IndexedTriangleArray.java b/src/javax/media/j3d/IndexedTriangleArray.java new file mode 100644 index 0000000..35969f0 --- /dev/null +++ b/src/javax/media/j3d/IndexedTriangleArray.java @@ -0,0 +1,222 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 3, or indexCount is not + * a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 3, or indexCount is not + * a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedTriangleArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1, + * or indexCount is less than 3, or indexCount is not + * a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedTriangleArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedTriangleArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleArrayRetained rt = (IndexedTriangleArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedTriangleArray t = new IndexedTriangleArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount()); + t.duplicateNodeComponent(this); + return t; + } +} diff --git a/src/javax/media/j3d/IndexedTriangleArrayRetained.java b/src/javax/media/j3d/IndexedTriangleArrayRetained.java new file mode 100644 index 0000000..22b4839 --- /dev/null +++ b/src/javax/media/j3d/IndexedTriangleArrayRetained.java @@ -0,0 +1,439 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + Point3d pnts[] = new Point3d[3]; + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + int[] vtxIndexArr = new int[3]; + + //NVaidya + // Bug 447: While loops below now traverse over all + // elements in the valid index range from initialIndexIndex + // to initialIndexInex + validIndexCount - 1 + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + pnts[0] = new Point3d(); + pnts[1] = new Point3d(); + pnts[2] = new Point3d(); + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectBoundingPolytope(pnts, bpolytope, + sdist,iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectCylinder(pnts, pickCylinder, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < loopStopIndex) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = indexCoord[i]; + getVertexData(indexCoord[i++], pnts[j]); + } + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + // intersect pnts[] with every triangle in this object + @Override + boolean intersect(Point3d[] pnts) { + Point3d[] points = new Point3d[3]; + double dist[] = new double[1]; + //NVaidya + // Bug 447: correction for loop indices + int i = initialIndexIndex; + int loopStopIndex = initialIndexIndex + validIndexCount; + + points[0] = new Point3d(); + points[1] = new Point3d(); + points[2] = new Point3d(); + + switch (pnts.length) { + case 3: // Triangle + while (i + * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])} + * for more exceptions that can be thrown + */ + 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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedTriangleFanArray object using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedTriangleFanArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount, + int[] stripIndexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedTriangleFanArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleFanArrayRetained rt = + (IndexedTriangleFanArrayRetained) retained; + int stripIndexCounts[] = new int[rt.getNumStrips()]; + rt.getStripIndexCounts(stripIndexCounts); + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedTriangleFanArray t = new IndexedTriangleFanArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount(), + stripIndexCounts); + t.duplicateNodeComponent(this); + return t; + } + +} diff --git a/src/javax/media/j3d/IndexedTriangleFanArrayRetained.java b/src/javax/media/j3d/IndexedTriangleFanArrayRetained.java new file mode 100644 index 0000000..f4f0c98 --- /dev/null +++ b/src/javax/media/j3d/IndexedTriangleFanArrayRetained.java @@ -0,0 +1,512 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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(); + int[] vtxIndexArr = new int[3]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectBoundingSphere(pnts, bsphere, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectBoundingPolytope(pnts, bpolytope, + sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + // intersect pnts[] with every triangle in this object + @Override + boolean intersect(Point3d[] pnts) { + int j; + 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; + } + + @Override + 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 + @Override + 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; + } + + @Override + int getClassType() { + return TRIANGLE_TYPE; + } +} diff --git a/src/javax/media/j3d/IndexedTriangleStripArray.java b/src/javax/media/j3d/IndexedTriangleStripArray.java new file mode 100644 index 0000000..71f4659 --- /dev/null +++ b/src/javax/media/j3d/IndexedTriangleStripArray.java @@ -0,0 +1,248 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])} + * for more exceptions that can be thrown + */ + 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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty IndexedTriangleStripArray object using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param indexCount + * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)} + * for a description of this parameter. + * + * @param stripIndexCounts + * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])} + * for a description of this parameter. + * + * @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 + * ;
+ * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public IndexedTriangleStripArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int indexCount, + int[] stripIndexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + 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. + */ + @Override + void createRetained() { + this.retained = new IndexedTriangleStripArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleStripArrayRetained rt = + (IndexedTriangleStripArrayRetained) retained; + int stripIndexCounts[] = new int[rt.getNumStrips()]; + rt.getStripIndexCounts(stripIndexCounts); + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + IndexedTriangleStripArray t = new IndexedTriangleStripArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + rt.getIndexCount(), + stripIndexCounts); + t.duplicateNodeComponent(this); + return t; + } +} diff --git a/src/javax/media/j3d/IndexedTriangleStripArrayRetained.java b/src/javax/media/j3d/IndexedTriangleStripArrayRetained.java new file mode 100644 index 0000000..3b393d3 --- /dev/null +++ b/src/javax/media/j3d/IndexedTriangleStripArrayRetained.java @@ -0,0 +1,533 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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(); + int[] vtxIndexArr = new int[3]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectBoundingSphere(pnts, bsphere, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectBoundingPolytope(pnts, bpolytope, + sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < stripIndexCounts.length) { + for(int k=0; k<2; k++) { + vtxIndexArr[k] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[k]); + } + scount = stripIndexCounts[i++]; + for (j=2; j < scount; j++) { + vtxIndexArr[2] = indexCoord[count]; + getVertexData(indexCoord[count++], pnts[2]); + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + // intersect pnts[] with every triangle in this object + @Override + boolean intersect(Point3d[] pnts) { + int j; + 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; + } + + @Override + 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 + @Override + 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; + } + + @Override + int getClassType() { + return TRIANGLE_TYPE; + } +} diff --git a/src/javax/media/j3d/IndexedUnorderSet.java b/src/javax/media/j3d/IndexedUnorderSet.java new file mode 100644 index 0000000..d498bac --- /dev/null +++ b/src/javax/media/j3d/IndexedUnorderSet.java @@ -0,0 +1,613 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * Use the following code to iterate through an array. + * + *

+ *  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];
+ *      ....
+ *  }
+ * 
+ * + *

+ * Note: + *

    + * 1) The array return is a copied of internal array.
    + * 2) Don't use arr.length , use IUset.arraySize();
    + * 3) IndexedObject contains an array of listIndex, the number of + * array elements depends on the number of different types of + * IndexedUnorderSet that use it.
    + * 4) No need to do casting for individual element as in ArrayList.
    + * 5) IndexedUnorderSet is thread safe.
    + * 6) Object implement this interface MUST initialize the index to -1.
    + *
+ * + *

+ * Limitation: + *

    + * 1) Order of IndexedObject in list is not important
    + * 2) Can't modify the clone() copy.
    + * 3) IndexedObject can't be null
    + *
+ */ + +class IndexedUnorderSet implements Cloneable, java.io.Serializable { + + // XXXX: 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 true if this list has no elements; + * false otherwise. + */ + final boolean isEmpty() { + return size == 0; + } + + /** + * Returns true 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 equals method. + * + * @param o an object. + * @return the index of the first occurrence of the argument in this + * list; returns -1 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 ArrayList instance. (The + * elements themselves are not copied.) + * + * @return a clone of this ArrayList instance. + */ + @Override + 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 ArrayList instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an ArrayList 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 (index + * < 0 || index >= size()). + */ + 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 + * (index < 0 || index >= size()). + */ + 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.err.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.err.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 (index + * < 0 || index >= size()). + */ + synchronized final void remove(int index) { + IndexedObject elm = elementData[index]; + + int univIdx = elm.getIdxUsed(univ); + + if (debug) { + if (elm.listIdx[univIdx][listType] != index) { + System.err.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 (index + * < 0 || index >= size()). + */ + 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.err.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 ArrayList instance to a stream (that + * is, serialize it). + * + * @serialData The length of the array backing the ArrayList + * instance is emitted (int), followed by all of its elements + * (each an Object) 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; iArrayList 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 + * NOTE: this method should not 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/javax/media/j3d/InputDeviceBlockingThread.java b/src/javax/media/j3d/InputDeviceBlockingThread.java new file mode 100644 index 0000000..f65a65b --- /dev/null +++ b/src/javax/media/j3d/InputDeviceBlockingThread.java @@ -0,0 +1,120 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 volatile boolean running = true; + private volatile boolean stop = false; + private boolean waiting = false; + private boolean ready = 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; + } + + + @Override + 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; + runMonitor(STOP); + } + + synchronized void runMonitor(int action) { + + switch (action) { + case WAIT: + // Issue 279 - loop until ready + while (running && !ready) { + waiting = true; + try { + wait(); + } catch (InterruptedException e) {} + waiting = false; + } + ready = false; + break; + case NOTIFY: + ready = true; + if (waiting) { + notify(); + } + break; + case STOP: + running = false; + if (waiting) { + notify(); + } + break; + } + } +} diff --git a/src/javax/media/j3d/InputDeviceScheduler.java b/src/javax/media/j3d/InputDeviceScheduler.java new file mode 100644 index 0000000..1c24094 --- /dev/null +++ b/src/javax/media/j3d/InputDeviceScheduler.java @@ -0,0 +1,214 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +/** + * 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; + + 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); + } + + @Override + 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) { + for (InputDevice each : physicalEnv.devices) { + addInputDevice(each); + } + 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; + } + + @Override + void doWork(long referenceTime) { + synchronized (nonBlockingDevices) { + for (int i = nonBlockingDevices.size()-1; i >=0; i--) { + ((InputDevice)nonBlockingDevices.get(i)).pollAndProcessInput(); + } + } + } + + @Override + 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(); + } + +} diff --git a/src/javax/media/j3d/IntegerFreeList.java b/src/javax/media/j3d/IntegerFreeList.java new file mode 100644 index 0000000..db96457 --- /dev/null +++ b/src/javax/media/j3d/IntegerFreeList.java @@ -0,0 +1,56 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + } + + @Override + synchronized Object getObject() { + if (size > 0) return super.removeLastElement(); + else return new Integer(++count); + } + + @Override + public synchronized void clear() { + super.clear(); + count = 0; + } + +} diff --git a/src/javax/media/j3d/Interpolator.java b/src/javax/media/j3d/Interpolator.java new file mode 100644 index 0000000..e035927 --- /dev/null +++ b/src/javax/media/j3d/Interpolator.java @@ -0,0 +1,140 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + + +/** + * 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 = 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. + */ + @Override + public void initialize() { + // Reset alpha + //alpha.setStartTime(J3dClock.currentTimeMillis()); + + // Insert wakeup condition into queue + wakeupOn(defaultWakeupCriterion); + } + + + /** + * Copies all Interpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/J3DBuffer.java b/src/javax/media/j3d/J3DBuffer.java new file mode 100644 index 0000000..2cacf8a --- /dev/null +++ b/src/javax/media/j3d/J3DBuffer.java @@ -0,0 +1,220 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.nio.ShortBuffer; + +/** + * 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 + * rewind on the read-only view, so that elements 0 + * through buffer.limit()-1 will be available internally. + * + * @see GeometryArray#setCoordRefBuffer(J3DBuffer) + * @see GeometryArray#setColorRefBuffer(J3DBuffer) + * @see GeometryArray#setNormalRefBuffer(J3DBuffer) + * @see GeometryArray#setTexCoordRefBuffer(int,J3DBuffer) + * @see GeometryArray#setVertexAttrRefBuffer(int,J3DBuffer) + * @see GeometryArray#setInterleavedVertexBuffer(J3DBuffer) + * @see CompressedGeometry#CompressedGeometry(CompressedGeometryHeader,J3DBuffer) + * + * @since Java 3D 1.3 + */ + +public class J3DBuffer { + +enum Type { + NULL, + UNKNOWN, + BYTE, + CHAR, + SHORT, + INT, + LONG, + FLOAT, + DOUBLE, +} + + private Buffer originalBuffer = null; + private Buffer readonlyBuffer = null; + Type 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(Buffer buffer) { + 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(Buffer buffer) { + Type bType = Type.NULL; + boolean direct = false; + ByteOrder order = ByteOrder.BIG_ENDIAN; + + if (buffer == null) { + bType = Type.NULL; + } + else if (buffer instanceof ByteBuffer) { + bType = Type.BYTE; + direct = ((ByteBuffer)buffer).isDirect(); + order = ((ByteBuffer)buffer).order(); + } + else if (buffer instanceof CharBuffer) { + bType = Type.CHAR; + direct = ((CharBuffer)buffer).isDirect(); + order = ((CharBuffer)buffer).order(); + } + else if (buffer instanceof ShortBuffer) { + bType = Type.SHORT; + direct = ((ShortBuffer)buffer).isDirect(); + order = ((ShortBuffer)buffer).order(); + } + else if (buffer instanceof IntBuffer) { + bType = Type.INT; + direct = ((IntBuffer)buffer).isDirect(); + order = ((IntBuffer)buffer).order(); + } + else if (buffer instanceof LongBuffer) { + bType = Type.LONG; + direct = ((LongBuffer)buffer).isDirect(); + order = ((LongBuffer)buffer).order(); + } + else if (buffer instanceof FloatBuffer) { + bType = Type.FLOAT; + direct = ((FloatBuffer)buffer).isDirect(); + order = ((FloatBuffer)buffer).order(); + } + else if (buffer instanceof DoubleBuffer) { + bType = Type.DOUBLE; + direct = ((DoubleBuffer)buffer).isDirect(); + order = ((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 != 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 BYTE: + ByteBuffer byteBuffer = ((ByteBuffer)buffer).asReadOnlyBuffer(); + byteBuffer.rewind(); + readonlyBuffer = byteBuffer; + break; + case FLOAT: + FloatBuffer floatBuffer = ((FloatBuffer)buffer).asReadOnlyBuffer(); + floatBuffer.rewind(); + readonlyBuffer = floatBuffer; + break; + case DOUBLE: + DoubleBuffer doubleBuffer = ((DoubleBuffer)buffer).asReadOnlyBuffer(); + doubleBuffer.rewind(); + readonlyBuffer = doubleBuffer; + break; + default: + readonlyBuffer = null; + } + } + + + /** + * Retrieves the NIO buffer object from this J3DBuffer. + * + * @return the current NIO buffer wrapped by this J3DBuffer + */ + public Buffer getBuffer() { + return originalBuffer; + } + +/** + * Gets the readonly view of the nio buffer we wrapped with J3DBuffer + * @return + */ +Buffer getROBuffer() { + return readonlyBuffer; +} +} diff --git a/src/javax/media/j3d/J3DGraphics2D.java b/src/javax/media/j3d/J3DGraphics2D.java new file mode 100644 index 0000000..1fc48cf --- /dev/null +++ b/src/javax/media/j3d/J3DGraphics2D.java @@ -0,0 +1,192 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +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. + * + *

+ * 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. + * + *

+ * 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 flush(boolean) method to ensure + * that the rendering actually happens. The flush method is implicitly + * called in the following cases: + * + *

    + *
  • The Canvas3D.swap method calls + * flush(true)
  • + *
  • The Java 3D renderer calls flush(true) prior to + * swapping the buffer for a double buffered on-screen Canvas3D
  • + *
  • The Java 3D renderer calls flush(true) prior to + * copying into the off-screen buffer of an off-screen Canvas3D
  • + *
  • The Java 3D renderer calls flush(false) after + * calling the preRender, renderField, postRender, and postSwap + * Canvas3D callback methods.
  • + *
+ * + *

+ * 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 drawImage(...) + * followed by flush(false), 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/javax/media/j3d/J3DGraphics2DImpl.java b/src/javax/media/j3d/J3DGraphics2DImpl.java new file mode 100644 index 0000000..0d55383 --- /dev/null +++ b/src/javax/media/j3d/J3DGraphics2DImpl.java @@ -0,0 +1,1214 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * Implementation class for J3DGraphics2D + */ + +final class J3DGraphics2DImpl extends J3DGraphics2D { + private boolean hasBeenDisposed = false; + 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 volatile boolean threadWaiting = false; + static final Color blackTransparent = new Color(0,0,0,0); + int objectId = -1; + + // Package scope contructor + J3DGraphics2DImpl(Canvas3D c) { + canvas3d = c; + + synchronized (VirtualUniverse.mc.contextCreationLock) { + if (c.ctx == null) { + // 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. + */ + @Override + public void flush(boolean waiting) { + + if (hasBeenDisposed) { + throw new IllegalStateException(J3dI18N.getString("J3DGraphics2D0")); + } + + 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 + // XXXX: 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() { + assert !hasBeenDisposed; + + // clip to offscreen buffer size + if (canvas3d.ctx == null) { + 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) { + + assert !hasBeenDisposed; + + 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 = new J3dMessage(); + 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 + @Override + public final void clip(Shape s) { + offScreenGraphics2D.clip(s); + } + + @Override + public FontMetrics getFontMetrics() { + return offScreenGraphics2D.getFontMetrics(); + } + + @Override + public Rectangle getClipBounds(Rectangle r) { + return offScreenGraphics2D.getClipBounds(r); + } + + @Override + public Rectangle getClipRect() { + return offScreenGraphics2D.getClipRect(); + } + + @Override + public String toString() { + return offScreenGraphics2D.toString(); + + } + + @Override + public final AffineTransform getTransform() { + return offScreenGraphics2D.getTransform(); + } + + @Override + public final Color getColor() { + return offScreenGraphics2D.getColor(); + } + + @Override + public final Composite getComposite() { + return offScreenGraphics2D.getComposite(); + } + + @Override + public final Font getFont() { + return offScreenGraphics2D.getFont(); + } + + @Override + public final FontMetrics getFontMetrics(Font f) { + return offScreenGraphics2D.getFontMetrics(f); + } + + @Override + public final FontRenderContext getFontRenderContext() { + return offScreenGraphics2D.getFontRenderContext(); + } + + @Override + public final GraphicsConfiguration getDeviceConfiguration() { + return offScreenGraphics2D.getDeviceConfiguration(); + } + + @Override + public final Object getRenderingHint(Key hintKey) { + return offScreenGraphics2D.getRenderingHint(hintKey); + } + + @Override + public final Paint getPaint() { + return offScreenGraphics2D.getPaint(); + } + + @Override + public final Rectangle getClipBounds() { + return offScreenGraphics2D.getClipBounds(); + } + + @Override + public final RenderingHints getRenderingHints() { + return offScreenGraphics2D.getRenderingHints(); + } + + @Override + public final Shape getClip() { + return offScreenGraphics2D.getClip(); + } + + @Override + public final Stroke getStroke() { + return offScreenGraphics2D.getStroke(); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + public final boolean hit(Rectangle rect, Shape s, boolean onStroke) { + return offScreenGraphics2D.hit(rect, s, onStroke); + } + + @Override + public final void addRenderingHints(Map hints) { + offScreenGraphics2D.addRenderingHints(hints); + } + + @Override + public final void clipRect(int x, int y, int width, int height) { + offScreenGraphics2D.clipRect(x, y, width, height); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + public final void drawArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawArc(x, y, width, height, startAngle, arcAngle); + } + + @Override + public final void drawGlyphVector(GlyphVector g, float x, float y) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawGlyphVector(g, x, y); + } + + @Override + 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 { + // XXXX: call validate with bounding box of primitive + // XXXX: Need to consider Stroke width + validate(); + } + offScreenGraphics2D.drawLine(x1, y1, x2, y2); + } + + @Override + public final void drawOval(int x, int y, int width, int height) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawOval(x, y, width, height); + } + + @Override + public final void drawPolygon(int xPoints[], int yPoints[], + int nPoints) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawPolygon(xPoints, yPoints, nPoints); + } + + @Override + public final void drawPolyline(int xPoints[], int yPoints[], + int nPoints) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawPolyline(xPoints, yPoints, nPoints); + } + + @Override + public final void drawRenderableImage(RenderableImage img, + AffineTransform xform) { + + validate(0, 0, img.getWidth(), img.getHeight(), xform); + offScreenGraphics2D.drawRenderableImage(img, xform); + } + + @Override + public final void drawRenderedImage(RenderedImage img, + AffineTransform xform) { + validate(0, 0, img.getWidth(), img.getHeight(), xform); + offScreenGraphics2D.drawRenderedImage(img, xform); + } + + @Override + public final void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawRoundRect(x, y, width, height, arcWidth, + arcHeight); + } + + @Override + public final void drawString(AttributedCharacterIterator iterator, + int x, int y) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawString(iterator, x, y); + } + + @Override + public final void drawString(AttributedCharacterIterator iterator, + float x, float y) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawString(iterator, x, y); + } + + @Override + 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); + + } + + @Override + public final void drawString(String s, int x, int y) { + drawString(s, (float) x, (float) y); + } + + @Override + 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); + } + + @Override + public final void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.fillArc(x, y, width, height, startAngle, arcAngle); + } + + @Override + public final void fillOval(int x, int y, int width, int height) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.fillOval(x, y, width, height); + } + + @Override + public final void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.fillRoundRect(x, y, width, height, arcWidth, + arcHeight); + } + + @Override + public final void rotate(double theta) { + offScreenGraphics2D.rotate(theta); + } + + @Override + public final void rotate(double theta, double x, double y) { + offScreenGraphics2D.rotate(theta, x, y); + } + + @Override + public final void scale(double sx, double sy) { + offScreenGraphics2D.scale(sx, sy); + } + + @Override + public final void setClip(Shape clip) { + offScreenGraphics2D.setClip(clip); + } + + + @Override + public final void setClip(int x, int y, int width, int height) { + offScreenGraphics2D.setClip(x, y, width, height); + } + + @Override + public final void setColor(Color c) { + offScreenGraphics2D.setColor(c); + } + + @Override + public final void setComposite(Composite comp) { + offScreenGraphics2D.setComposite(comp); + } + + @Override + public final void setFont(Font font) { + offScreenGraphics2D.setFont(font); + } + + @Override + public final void setPaint( Paint paint ) { + offScreenGraphics2D.setPaint(paint); + } + + @Override + public final void setPaintMode() { + xOrModeColor = null; + offScreenGraphics2D.setPaintMode(); + } + + @Override + public final void setRenderingHint(Key hintKey, Object hintValue) { + offScreenGraphics2D.setRenderingHint(hintKey, hintValue); + } + + @Override + public final void setRenderingHints(Map hints) { + offScreenGraphics2D.setRenderingHints(hints); + } + + @Override + public final void setStroke(Stroke s) { + strokeSet = (s != null); + offScreenGraphics2D.setStroke(s); + } + + @Override + public final void setTransform(AffineTransform Tx) { + offScreenGraphics2D.setTransform(Tx); + } + + @Override + public final void setXORMode(Color c1) { + xOrModeColor = c1; + offScreenGraphics2D.setXORMode(c1); + } + + @Override + public final void shear(double shx, double shy) { + offScreenGraphics2D.shear(shx, shy); + } + + @Override + public final void transform(AffineTransform Tx) { + offScreenGraphics2D.transform(Tx); + } + + @Override + public final void translate(double tx, double ty) { + offScreenGraphics2D.translate(tx, ty); + } + + @Override + public final void translate(int x, int y) { + offScreenGraphics2D.translate(x, y); + } + @Override + public boolean hitClip(int x, int y, int width, int height) { + return offScreenGraphics2D.hitClip(x, y, width, height); + } + + @Override + public void draw3DRect(int x, int y, int width, int height, + boolean raised) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.draw3DRect(x, y, width, height, raised); + } + + @Override + public void drawBytes(byte data[], int offset, int length, int x, int y) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawBytes(data, offset, length, x, y); + } + + @Override + public void drawChars(char data[], int offset, int length, int x, int y) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawChars(data, offset, length, x, y); + } + + @Override + public void drawPolygon(Polygon p) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.drawPolygon(p); + } + + @Override + public void drawRect(int x, int y, int width, int height) { + // XXXX: call validate with bounding box of primitive + // XXXX: need to consider Stroke width + validate(); + offScreenGraphics2D.drawRect(x, y, width, height); + } + + @Override + public void fill3DRect(int x, int y, int width, int height, + boolean raised) { + // XXXX: call validate with bounding box of primitive + // XXXX: need to consider Stroke width + validate(); + offScreenGraphics2D.fill3DRect(x, y, width, height, raised); + } + + @Override + public void fillPolygon(Polygon p) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.fillPolygon(p); + } + + @Override + public final void fillPolygon(int xPoints[], int yPoints[], + int nPoints) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.fillPolygon(xPoints, yPoints, nPoints); + } + + @Override + public final void fillRect(int x, int y, int width, int height) { + // XXXX: call validate with bounding box of primitive + validate(); + offScreenGraphics2D.fillRect(x, y, width, height); + } + + // Issue 121 - release all resources, mark as disposed + @Override + public void dispose() { + + // Issue 583 - do nothing if graphics has already been disposed + if (hasBeenDisposed) { + return; + } + + if (Thread.currentThread() == canvas3d.screen.renderer) { + doDispose(); + } else { + // Behavior Scheduler or other threads + // XXXX: 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.DISPOSE2D, + null, null, null); + } + + + } + + public void doDispose() { + + if (hasBeenDisposed) { + return; + } + + if (objectId != -1) { + Canvas3D.freeTexture(canvas3d.ctx, objectId); + objectId = -1; + } + + // Dispose of the underlying Graphics2D + offScreenGraphics2D.dispose(); + + // Mark as disposed + hasBeenDisposed = true; + // Issue 583 - set graphics2D field to null so it will get recreated + canvas3d.graphics2D = null; + } + + @Override + public void drawAndFlushImage(BufferedImage img, int x, int y, + ImageObserver observer) { + + if (hasBeenDisposed) { + throw new IllegalStateException(J3dI18N.getString("J3DGraphics2D0")); + } + + 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 + // XXXX: 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) { + + assert !hasBeenDisposed; + + int imgWidth = img.getWidth(observer); + int imgHeight = img.getHeight(observer); + int px, py, x1, y1, x2, y2; + + if (canvas3d.ctx == null) { + 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 (!initTexMap) { + if (objectId == -1) { + objectId = Canvas3D.generateTexID(canvas3d.ctx); + } + 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; + Canvas3D.freeTexture(canvas3d.ctx, objectId); + objectId = -1; + // TODO : Need to find a better way to report no resource problem --- Chien. + System.err.println("J3DGraphics2DImpl.copyDataToCanvas() : Fail to get texture resources ..."); + + } else { + initTexMap = true; + } + } + if (initTexMap) { + canvas3d.texturemapping(canvas3d.ctx, px, py, + x1, y1, x2, y2, + texWidth, texHeight, w, + (abgr ? ImageComponentRetained.TYPE_BYTE_ABGR: + ImageComponentRetained.TYPE_BYTE_RGBA), + objectId, 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) { + // Issue 279 - loop until ready + while (threadWaiting) { + try { + wait(); + } catch (InterruptedException e){} + } + } else if (action == J3dThread.NOTIFY) { + notify(); + threadWaiting = false; + } + } +} diff --git a/src/javax/media/j3d/J3dClock.java b/src/javax/media/j3d/J3dClock.java new file mode 100644 index 0000000..b634f79 --- /dev/null +++ b/src/javax/media/j3d/J3dClock.java @@ -0,0 +1,96 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Utility class to provide a more accurate replacement for + * System.currentTimeMillis(). + */ +class J3dClock { + + // Issue 543 - Flag to indicate whether clock skews are taken in account + // and corrected. false by default. Set by the "j3d.adjustClockSkew" property. + private static boolean skewAdjustedClock = false; + + private static long deltaTime; + private static final long nsecPerMsec = 1000000; + + /** + * Private constructor, since no instance should ever be created. + */ + private J3dClock() { + } + + /** + * Returns the current time in milliseconds. This is a more + * accurate version of System.currentTimeMillis and should be used in + * its place. + * + * @return the current time in milliseconds. + */ + static long currentTimeMillis() { + if (!skewAdjustedClock) { + return (System.nanoTime() / nsecPerMsec) + deltaTime; + } else { + // Issue 543 - Adjust for possible clock skew + long time = (System.nanoTime() / nsecPerMsec) + deltaTime; + long sysTime = System.currentTimeMillis(); + if (Math.abs(time - sysTime) > 50) { + long baseTime, baseTimerValue; + synchronized (J3dClock.class) { + baseTime = System.currentTimeMillis(); + baseTimerValue = System.nanoTime(); + } + deltaTime = baseTime - (baseTimerValue / nsecPerMsec); + time = (System.nanoTime() / nsecPerMsec) + deltaTime; + } + return time; + } + } + + static { + // Issue 543: get property for clock skew adjustment + skewAdjustedClock = MasterControl.getBooleanProperty("j3d.adjustClockSkew", + skewAdjustedClock, "clock skew adjustment"); + + // Call time methods once without using their values to ensure that + // the methods are "warmed up". We need to make sure that the actual + // calls that we use take place as close together as possible in time. + System.currentTimeMillis(); + System.nanoTime(); + + // Compute deltaTime between System.currentTimeMillis() + // and the high-res timer, use a synchronized block to force both calls + // to be made before the integer divide + long baseTime, baseTimerValue; + synchronized (J3dClock.class) { + baseTime = System.currentTimeMillis(); + baseTimerValue = System.nanoTime(); + } + deltaTime = baseTime - (baseTimerValue / nsecPerMsec); + } +} diff --git a/src/javax/media/j3d/J3dDebug.java b/src/javax/media/j3d/J3dDebug.java new file mode 100644 index 0000000..1b18544 --- /dev/null +++ b/src/javax/media/j3d/J3dDebug.java @@ -0,0 +1,453 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +class J3dDebug { + + // For production release devPhase is 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 = VersionInfo.isDevPhase; + + // 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 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 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 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.err.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.err.println("WARNING: Package.getPackage(" + + pkgName + + ") is null"); + } + else { + if(devPhase && debug) { + System.err.println(p); + System.err.println("Specification Title = " + + p.getSpecificationTitle()); + System.err.println("Specification Vendor = " + + p.getSpecificationVendor()); + System.err.println("Specification Version = " + + p.getSpecificationVersion()); + System.err.println("Implementation Vendor = " + + p.getImplementationVendor()); + System.err.println("Implementation Version = " + + p.getImplementationVersion()); + } + else if(devPhase) + System.err.println(", Java 3D " + p.getImplementationVersion() + "."); + } + } + catch (ClassNotFoundException e) { + System.err.println("Unable to load " + pkgName); + } + + // System.err.println(); + } + + + static { + // initialize the debug flag + debug = false; + } + +} + diff --git a/src/javax/media/j3d/J3dHash.java b/src/javax/media/j3d/J3dHash.java new file mode 100644 index 0000000..5db0437 --- /dev/null +++ b/src/javax/media/j3d/J3dHash.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Harvey Harrison + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + */ +package javax.media.j3d; + +/** + * A Utility class wrapping the approach used to hash double values in Java3D + */ +class J3dHash { + +// prevent an instance from actually being created +private J3dHash() {} + +/** + * Mix the given double into the provided long hash. + */ +static final long mixDoubleBits(long hash, double d) { + hash *= 31L; + // Treat 0.0d and -0.0d the same (all zero bits) + if (d == 0.0d) + return hash; + + return hash + Double.doubleToLongBits(d); +} + +/** + * Return an integer hash from a long by mixing it with itself. + */ +static final int finish(long hash) { + return (int)(hash ^ (hash >> 32)); +} +} diff --git a/src/javax/media/j3d/J3dI18N.java b/src/javax/media/j3d/J3dI18N.java new file mode 100644 index 0000000..c6ddf21 --- /dev/null +++ b/src/javax/media/j3d/J3dI18N.java @@ -0,0 +1,45 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + + +class J3dI18N { + static String getString(String key) { + String s; + try { + s = 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/javax/media/j3d/J3dMessage.java b/src/javax/media/j3d/J3dMessage.java new file mode 100644 index 0000000..4c695f3 --- /dev/null +++ b/src/javax/media/j3d/J3dMessage.java @@ -0,0 +1,185 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + static final int CREATE_OFFSCREENBUFFER = 61; + static final int DESTROY_CTX_AND_OFFSCREENBUFFER = 62; + static final int SHADER_ATTRIBUTE_CHANGED = 63; + static final int SHADER_ATTRIBUTE_SET_CHANGED = 64; + static final int SHADER_APPEARANCE_CHANGED = 65; + static final int ALLOCATE_CANVASID = 66; + static final int FREE_CANVASID = 67; + + /** + * 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.err.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(); + } + } + + final synchronized int getRefcount() { + return refcount; + } +} diff --git a/src/javax/media/j3d/J3dNotification.java b/src/javax/media/j3d/J3dNotification.java new file mode 100644 index 0000000..c1b0e70 --- /dev/null +++ b/src/javax/media/j3d/J3dNotification.java @@ -0,0 +1,58 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * J3dNotification is used to hold data for asynchronous error notification. + */ + +class J3dNotification extends Object { + /** + * The various notification types. + */ + static final int INVALID_TYPE = -1; + static final int SHADER_ERROR = 0; + static final int RENDERING_ERROR = 1; + + /** + * This holds the type of this message + */ + int type = INVALID_TYPE; + + /** + * The universe that this message originated from + */ + VirtualUniverse universe; + + /** + * The arguements for a message, 6 for now + */ + static final int MAX_ARGS = 6; + + Object[] args = new Object[MAX_ARGS]; + +} diff --git a/src/javax/media/j3d/J3dQueryProps.java b/src/javax/media/j3d/J3dQueryProps.java new file mode 100644 index 0000000..ef639f1 --- /dev/null +++ b/src/javax/media/j3d/J3dQueryProps.java @@ -0,0 +1,138 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Set; + + +/** + * 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(ArrayList keys, ArrayList values) { + table = new Hashtable(); + for (int i = 0; i < keys.size(); i++) { + table.put(keys.get(i), values.get(i)); + } + } + + /** + * Gets value corresponding to specified key + */ + @Override + public Object get(Object key) { + return table.get(key); + } + + /** + * Returns true if the specified key is contained in this Map + */ + @Override + public boolean containsKey(Object key) { + return table.containsKey(key); + } + + /** + * Returns true if the specified value is contained in this Map + */ + @Override + public boolean containsValue(Object value) { + return table.containsValue(value); + } + + /** + * Returns a new Set object for the entries of this map + */ + @Override + public Set entrySet() { + if (entrySet == null) + entrySet = new EntrySet(); + + return entrySet; + } + + + /** + * Entry set class + */ + private class EntrySet extends AbstractSet { + private EntrySet() { + } + + @Override + public int size() { + return table.size(); + } + + @Override + public Iterator iterator() { + return new MapIterator(); + } + } + + + /** + * Entry set class + */ + private class MapIterator implements Iterator { + private Iterator i; + + private MapIterator() { + i = table.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return i.hasNext(); + } + + @Override + public Object next() { + return i.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/javax/media/j3d/J3dStructure.java b/src/javax/media/j3d/J3dStructure.java new file mode 100644 index 0000000..4dcf45f --- /dev/null +++ b/src/javax/media/j3d/J3dStructure.java @@ -0,0 +1,165 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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/javax/media/j3d/J3dThread.java b/src/javax/media/j3d/J3dThread.java new file mode 100644 index 0000000..1d5b3a8 --- /dev/null +++ b/src/javax/media/j3d/J3dThread.java @@ -0,0 +1,354 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 volatile boolean running = true; + + /** + * This flag is set by the RUN action of runMonitor to indicate that the + * waiting thread has work to do. + */ + private volatile boolean ready = false; + + /** + * The thread data for this thread + */ + private J3dThreadData[] data = null; + + /** + * This indicates that this thread is started and able to accept work + */ + 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 + */ + private 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 0) { + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + for (int k = 0; k < texCoordSetMapLen; k++) { + if (texCoordSetMapOffset[k] != -1) { + int off = texCoordoff + texCoordSetMapOffset[k]; + gl.glMultiTexCoord2f(GL.GL_TEXTURE0 + k, + varray[off], + varray[off + 1]); + } + } + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + for (int k = 0; k < texCoordSetMapLen; k++) { + if (texCoordSetMapOffset[k] != -1) { + int off = texCoordoff + texCoordSetMapOffset[k]; + gl.glMultiTexCoord3f(GL.GL_TEXTURE0 + k, + varray[off], + varray[off + 1], + varray[off + 2]); + } + } + } else { + for (int k = 0; k < texCoordSetMapLen; k++) { + if (texCoordSetMapOffset[k] != -1) { + int off = texCoordoff + texCoordSetMapOffset[k]; + gl.glMultiTexCoord4f(GL.GL_TEXTURE0 + k, + varray[off], + varray[off + 1], + varray[off + 2], + varray[off + 3]); + } + } + } + } else { // no multitexture + if (texCoordSetMapOffset[0] != -1) { + int off = texCoordoff + texCoordSetMapOffset[0]; + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + gl.glTexCoord2f(varray[off], varray[off + 1]); + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + gl.glTexCoord3f(varray[off], varray[off + 1], varray[off + 2]); + } else { + gl.glTexCoord4f(varray[off], varray[off + 1], varray[off + 2], varray[off + 3]); + } + } + } // no multitexture + } + // texCoordSetMapLen can't be 0 if texture coordinates + // is to be specified + } + + if ((vformat & GeometryArray.COORDINATES) != 0) { + if (xform != null) { + // transform the vertex data with the static transform + float w = (float) (xform[12] * varray[coordoff] + + xform[13] * varray[coordoff+1] + + xform[14] * varray[coordoff+2] + + xform[15]); + float winv = 1.0f/w; + float vx = (float) (xform[0] * varray[coordoff] + + xform[1] * varray[coordoff+1] + + xform[2] * varray[coordoff+2] + + xform[3]) * winv; + float vy = (float) (xform[4] * varray[coordoff] + + xform[5] * varray[coordoff+1] + + xform[6] * varray[coordoff+2] + + xform[7]) * winv; + float vz = (float) (xform[8] * varray[coordoff] + + xform[9] * varray[coordoff+1] + + xform[10] * varray[coordoff+2] + + xform[11]) * winv; + gl.glVertex3f(vx, vy, vz); + } else { + gl.glVertex3f(varray[coordoff], varray[coordoff + 1], varray[coordoff + 2]); + } + } + normoff += stride; + coloroff += stride; + coordoff += stride; + texCoordoff += stride; + vAttrOff += stride; + } + gl.glEnd(); + } + } else if ((geo_type == GeometryRetained.GEO_TYPE_QUAD_SET) || + (geo_type == GeometryRetained.GEO_TYPE_TRI_SET) || + (geo_type == GeometryRetained.GEO_TYPE_POINT_SET) || + (geo_type == GeometryRetained.GEO_TYPE_LINE_SET)) { + int primType = 0; + switch (geo_type) { + case GeometryRetained.GEO_TYPE_QUAD_SET : + primType = GL2.GL_QUADS; + break; + case GeometryRetained.GEO_TYPE_TRI_SET : + primType = GL.GL_TRIANGLES; + break; + case GeometryRetained.GEO_TYPE_POINT_SET : + primType = GL.GL_POINTS; + break; + case GeometryRetained.GEO_TYPE_LINE_SET : + primType = GL.GL_LINES; + break; + } + + if (ignoreVertexColors) { + vformat &= ~GeometryArray.COLOR; + } + + gl.glBegin(primType); + for (int j = 0; j < vcount; j++) { + if ((vformat & GeometryArray.NORMALS) != 0) { + if (nxform != null) { + float nx = (float) (nxform[0] * varray[normoff] + + nxform[1] * varray[normoff+1] + + nxform[2] * varray[normoff+2]); + float ny = (float) (nxform[4] * varray[normoff] + + nxform[5] * varray[normoff+1] + + nxform[6] * varray[normoff+2]); + float nz = (float) (nxform[8] * varray[normoff] + + nxform[9] * varray[normoff+1] + + nxform[10] * varray[normoff+2]); + gl.glNormal3f(nx, ny, nz); + } else { + gl.glNormal3f(varray[normoff], varray[normoff + 1], varray[normoff + 2]); + } + } + if ((vformat & GeometryArray.COLOR) != 0) { + if (useAlpha) { + float cr, cg, cb, ca; + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + cr = varray[coloroff]; + cg = varray[coloroff + 1]; + cb = varray[coloroff + 2]; + ca = varray[coloroff + 3] * alpha; + } else { + cr = varray[coloroff]; + cg = varray[coloroff + 1]; + cb = varray[coloroff + 2]; + ca = alpha; + } + gl.glColor4f(cr, cg, cb, ca); + } else { + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { // alpha is present + gl.glColor4f(varray[coloroff], + varray[coloroff + 1], + varray[coloroff + 2], + varray[coloroff + 3]); + } else { + gl.glColor3f(varray[coloroff], + varray[coloroff + 1], + varray[coloroff + 2]); + } + } + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vaOff = vAttrOff; + if (verts == null) { + verts = FloatBuffer.wrap(varray); + } + for (int vaIdx = 0; vaIdx < vertexAttrCount; vaIdx++) { + switch (vertexAttrSizes[vaIdx]) { + case 1: + verts.position(vaOff); + jctx.vertexAttr1fv(gl, vaIdx, verts); + break; + case 2: + verts.position(vaOff); + jctx.vertexAttr2fv(gl, vaIdx, verts); + break; + case 3: + verts.position(vaOff); + jctx.vertexAttr3fv(gl, vaIdx, verts); + break; + case 4: + verts.position(vaOff); + jctx.vertexAttr4fv(gl, vaIdx, verts); + break; + } + + vaOff += vertexAttrSizes[vaIdx]; + } + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + if (texCoordSetMapLen > 0) { + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + for (int k = 0; k < texCoordSetMapLen; k++) { + if (texCoordSetMapOffset[k] != -1) { + int off = texCoordoff + texCoordSetMapOffset[k]; + gl.glMultiTexCoord2f(GL.GL_TEXTURE0 + k, + varray[off], + varray[off + 1]); + } + } + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + for (int k = 0; k < texCoordSetMapLen; k++) { + if (texCoordSetMapOffset[k] != -1) { + int off = texCoordoff + texCoordSetMapOffset[k]; + gl.glMultiTexCoord3f(GL.GL_TEXTURE0 + k, + varray[off], + varray[off + 1], + varray[off + 2]); + } + } + } else { + for (int k = 0; k < texCoordSetMapLen; k++) { + if (texCoordSetMapOffset[k] != -1) { + int off = texCoordoff + texCoordSetMapOffset[k]; + gl.glMultiTexCoord4f(GL.GL_TEXTURE0 + k, + varray[off], + varray[off + 1], + varray[off + 2], + varray[off + 3]); + } + } + } + } else { // no multitexture + if (texCoordSetMapOffset[0] != -1) { + int off = texCoordoff + texCoordSetMapOffset[0]; + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + gl.glTexCoord2f(varray[off], varray[off + 1]); + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + gl.glTexCoord3f(varray[off], varray[off + 1], varray[off + 2]); + } else { + gl.glTexCoord4f(varray[off], varray[off + 1], varray[off + 2], varray[off + 3]); + } + } + } // no multitexture + } + // texCoordSetMapLen can't be 0 if texture coordinates is + // to be specified + } + + if ((vformat & GeometryArray.COORDINATES) != 0) { + if (xform != null) { + // transform the vertex data with the static transform + float w = (float) (xform[12] * varray[coordoff] + + xform[13] * varray[coordoff+1] + + xform[14] * varray[coordoff+2] + + xform[15]); + float winv = 1.0f/w; + float vx = (float) (xform[0] * varray[coordoff] + + xform[1] * varray[coordoff+1] + + xform[2] * varray[coordoff+2] + + xform[3]) * winv; + float vy = (float) (xform[4] * varray[coordoff] + + xform[5] * varray[coordoff+1] + + xform[6] * varray[coordoff+2] + + xform[7]) * winv; + float vz = (float) (xform[8] * varray[coordoff] + + xform[9] * varray[coordoff+1] + + xform[10] * varray[coordoff+2] + + xform[11]) * winv; + gl.glVertex3f(vx, vy, vz); + } else { + gl.glVertex3f(varray[coordoff], varray[coordoff + 1], varray[coordoff + 2]); + } + } + normoff += stride; + coloroff += stride; + coordoff += stride; + texCoordoff += stride; + vAttrOff += stride; + } + gl.glEnd(); + } + } + + // used to Build Dlist GeometryArray by Reference with java arrays + @Override + void buildGAForByRef(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, boolean updateAlpha, + float alpha, + boolean ignoreVertexColors, + int vcount, + int vformat, + int vdefined, + int initialCoordIndex, float[] vfcoords, double[] vdcoords, + int initialColorIndex, float[] cfdata, byte[] cbdata, + int initialNormalIndex, float[] ndata, + int vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndices, float[][] vertexAttrData, + int texCoordMapLength, + int[] tcoordsetmap, + int[] texIndices, int texStride, Object[] texCoords, + double[] xform, double[] nxform) { + if (VERBOSE) System.err.println("JoglPipeline.buildGAForByRef()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + FloatBuffer[] texCoordBufs = null; + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) { + vertexAttrBufs = getVertexAttrSetBuffer(vertexAttrData); + } + + // get texture arrays + if (textureDefined) { + texCoordBufs = getTexCoordSetBuffer(texCoords); + } + + // process alpha for geometryArray without alpha + boolean useAlpha = false; + if (updateAlpha && !ignoreVertexColors) { + useAlpha = true; + } + + int[] sarray = null; + int[] start_array = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) { + sarray = ((GeometryStripArrayRetained) geo).stripVertexCounts; + strip_len = sarray.length; + start_array = ((GeometryStripArrayRetained) geo).stripStartOffsetIndices; + } + + if (ignoreVertexColors) { + vformat &= ~GeometryArray.COLOR; + floatColorsDefined = false; + byteColorsDefined = false; + } + + // get coordinate array + if (floatCoordDefined) { + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + fverts = getVertexArrayBuffer(vfcoords, (xform == null)); + if (xform != null) { + // Must copy in and transform data + for (int i = initialCoordIndex; i < vcount * 3; i += 3) { + fverts.put(i , (float) (xform[0] * vfcoords[i] + + xform[1] * vfcoords[i+1] + + xform[2] * vfcoords[i+2])); + fverts.put(i+1, (float) (xform[4] * vfcoords[i] + + xform[5] * vfcoords[i+1] + + xform[6] * vfcoords[i+2])); + fverts.put(i+2, (float) (xform[8] * vfcoords[i] + + xform[9] * vfcoords[i+1] + + xform[10] * vfcoords[i+2])); + } + } + } else if (doubleCoordDefined) { + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + dverts = getVertexArrayBuffer(vdcoords, (xform == null)); + if (xform != null) { + // Must copy in and transform data + for (int i = initialCoordIndex; i < vcount * 3; i += 3) { + dverts.put(i , (xform[0] * vdcoords[i] + + xform[1] * vdcoords[i+1] + + xform[2] * vdcoords[i+2])); + dverts.put(i+1, (xform[4] * vdcoords[i] + + xform[5] * vdcoords[i+1] + + xform[6] * vdcoords[i+2])); + dverts.put(i+2, (xform[8] * vdcoords[i] + + xform[9] * vdcoords[i+1] + + xform[10] * vdcoords[i+2])); + } + } + } else { + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); + } + + // get color array + if (floatColorsDefined) { + gl.glEnableClientState(GL2.GL_COLOR_ARRAY); + fclrs = getColorArrayBuffer(cfdata, !useAlpha); + if (useAlpha) { + // Must copy in and modify color data + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + for (int i = initialColorIndex; i < vcount * 4; i += 4) { + fclrs.put(i , cfdata[i]); + fclrs.put(i+1, cfdata[i+1]); + fclrs.put(i+2, cfdata[i+2]); + fclrs.put(i+3, alpha * cfdata[i+3]); + } + } else { + int k = 0; + for (int i = initialColorIndex; i < vcount * 4; i += 4) { + fclrs.put(i , cfdata[k++]); + fclrs.put(i+1, cfdata[k++]); + fclrs.put(i+2, cfdata[k++]); + fclrs.put(i+3, alpha); + } + } + vformat |= GeometryArray.WITH_ALPHA; + } + } else if (byteColorsDefined) { + gl.glEnableClientState(GL2.GL_COLOR_ARRAY); + bclrs = getColorArrayBuffer(cbdata, !useAlpha); + if (useAlpha) { + // Must copy in and modify color data + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + for (int i = initialColorIndex; i < vcount * 4; i += 4) { + bclrs.put(i , cbdata[i]); + bclrs.put(i+1, cbdata[i+1]); + bclrs.put(i+2, cbdata[i+2]); + bclrs.put(i+3, (byte) (alpha * (int) (cbdata[i+3] & 0xFF))); + } + } else { + int k = 0; + for (int i = initialColorIndex; i < vcount * 4; i += 4) { + bclrs.put(i , cbdata[k++]); + bclrs.put(i+1, cbdata[k++]); + bclrs.put(i+2, cbdata[k++]); + bclrs.put(i+3, (byte) (alpha * 255.0f)); + } + } + vformat |= GeometryArray.WITH_ALPHA; + } + } else { + gl.glDisableClientState(GL2.GL_COLOR_ARRAY); + } + + // get normal array + if (normalsDefined) { + gl.glEnableClientState(GL2.GL_NORMAL_ARRAY); + norms = getNormalArrayBuffer(ndata, (nxform == null)); + if (nxform != null) { + // Must copy in and transform data + for (int i = initialNormalIndex; i < vcount * 3; i += 3) { + norms.put(i , (float) (nxform[0] * ndata[i] + + nxform[1] * ndata[i+1] + + nxform[2] * ndata[i+2])); + norms.put(i+1, (float) (nxform[4] * ndata[i] + + nxform[5] * ndata[i+1] + + nxform[6] * ndata[i+2])); + norms.put(i+2, (float) (nxform[8] * ndata[i] + + nxform[9] * ndata[i+1] + + nxform[10] * ndata[i+2])); + } + } + } else { + gl.glDisableClientState(GL2.GL_NORMAL_ARRAY); + } + + executeGeometryArrayVA(ctx, geo, geo_type, + isNonUniformScale, ignoreVertexColors, + vcount, vformat, vdefined, + initialCoordIndex, fverts, dverts, + initialColorIndex, fclrs, bclrs, + initialNormalIndex, norms, + vertexAttrCount, vertexAttrSizes, + vertexAttrIndices, vertexAttrBufs, + texCoordMapLength, + tcoordsetmap, texCoordMapLength, + texIndices, texStride, texCoordBufs, 0, + sarray, strip_len, start_array); + } + + //---------------------------------------------------------------------- + // Private helper methods for GeometryArrayRetained + // + + private void + testForInterleavedArrays(int vformat, + boolean[] useInterleavedArrays, + int[] iaFormat) { + if (VERBOSE) System.err.println("JoglPipeline.testForInterleavedArrays()"); + useInterleavedArrays[0] = true; + switch (vformat) { + case GeometryArray.COORDINATES : + iaFormat[0] = GL2.GL_V3F; break; + case (GeometryArray.COORDINATES | GeometryArray.NORMALS) : + iaFormat[0] = GL2.GL_N3F_V3F; break; + case (GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2) : + iaFormat[0] = GL2.GL_T2F_V3F; break; + case (GeometryArray.COORDINATES | GeometryArray.NORMALS | GeometryArray.COLOR) : + case (GeometryArray.COORDINATES | GeometryArray.NORMALS | GeometryArray.COLOR | GeometryArray.WITH_ALPHA) : + iaFormat[0] = GL2.GL_C4F_N3F_V3F; break; + case (GeometryArray.COORDINATES | GeometryArray.NORMALS | GeometryArray.TEXTURE_COORDINATE_2) : + iaFormat[0] = GL2.GL_T2F_N3F_V3F; break; + case (GeometryArray.COORDINATES | GeometryArray.NORMALS | GeometryArray.COLOR | GeometryArray.TEXTURE_COORDINATE_2): + case (GeometryArray.COORDINATES | GeometryArray.NORMALS | GeometryArray.COLOR | GeometryArray.WITH_ALPHA | GeometryArray.TEXTURE_COORDINATE_2): + iaFormat[0] = GL2.GL_T2F_C4F_N3F_V3F; break; + default: + useInterleavedArrays[0] = false; break; + } + } + + private void + enableTexCoordPointer(GL2 gl, + int texUnit, + int texSize, + int texDataType, + int stride, + Buffer pointer) { + if (VERBOSE) System.err.println("JoglPipeline.enableTexCoordPointer()"); + clientActiveTextureUnit(gl, texUnit); + gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + gl.glTexCoordPointer(texSize, texDataType, stride, pointer); + } + + private void disableTexCoordPointer(GL2 gl, int texUnit) { + if (VERBOSE) System.err.println("JoglPipeline.disableTexCoordPointer()"); + clientActiveTextureUnit(gl, texUnit); + gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + } + + private void clientActiveTextureUnit(GL2 gl, int texUnit) { + if (VERBOSE) System.err.println("JoglPipeline.clientActiveTextureUnit()"); + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glClientActiveTexture(texUnit + GL.GL_TEXTURE0); + } + } + + + private void + executeTexture(int texCoordSetMapLen, + int texSize, int bstride, int texCoordoff, + int[] texCoordSetMapOffset, + int numActiveTexUnit, + FloatBuffer verts, GL2 gl) { + if (VERBOSE) System.err.println("JoglPipeline.executeTexture()"); + int tus = 0; /* texture unit state index */ + + for (int i = 0; i < numActiveTexUnit; i++) { + + tus = i; + + /* + * it's possible thattexture unit state index (tus) + * is greater than the texCoordSetMapOffsetLen, in this + * case, just disable TexCoordPointer. + */ + if ((tus < texCoordSetMapLen) && + (texCoordSetMapOffset[tus] != -1)) { + if (EXTRA_DEBUGGING) { + System.err.println(" texCoord position " + i + ": " + (texCoordoff + texCoordSetMapOffset[tus])); + } + verts.position(texCoordoff + texCoordSetMapOffset[tus]); + enableTexCoordPointer(gl, i, + texSize, GL.GL_FLOAT, bstride, + verts); + } else { + disableTexCoordPointer(gl, i); + } + } + } + + private void resetTexture(GL2 gl, JoglContext ctx) { + if (VERBOSE) System.err.println("JoglPipeline.resetTexture()"); + /* Disable texture coordinate arrays for all texture units */ + for (int i = 0; i < ctx.getMaxTexCoordSets(); i++) { + disableTexCoordPointer(gl, i); + } + /* Reset client active texture unit to 0 */ + clientActiveTextureUnit(gl, 0); + } + + private void + executeGeometryArray(Context absCtx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int startVIndex, int vcount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetMapOffset, + int numActiveTexUnitState, + int vertexAttrCount, int[] vertexAttrSizes, + float[] varray, FloatBuffer varrayBuffer, + float[] carray, int cDirty) { + if (VERBOSE) System.err.println("JoglPipeline.executeGeometryArray()"); + JoglContext ctx = (JoglContext) absCtx; + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + boolean useInterleavedArrays; + int iaFormat = 0; + int primType = 0; + int stride = 0, coordoff = 0, normoff = 0, coloroff = 0, texCoordoff = 0; + int texSize = 0, texStride = 0; + int vAttrOff = 0; + int vAttrStride = 0; + int bstride = 0, cbstride = 0; + FloatBuffer verts = null; + FloatBuffer clrs = null; + int[] sarray = null; + int[] start_array = null; + + if (EXTRA_DEBUGGING) { + System.err.println("Vertex format: " + getVertexDescription(vformat)); + System.err.println("Geometry type: " + getGeometryDescription(geo_type)); + if (carray != null) { + System.err.println(" Separate color array"); + } else { + System.err.println(" Colors (if any) interleaved"); + } + } + + if ((vformat & GeometryArray.COORDINATES) != 0) { + stride += 3; + } + if ((vformat & GeometryArray.NORMALS) != 0) { + stride += 3; + coordoff += 3; + } + if ((vformat & GeometryArray.COLOR) != 0) { + if ((vformat & GeometryArray.WITH_ALPHA) != 0 ) { + stride += 4; + normoff += 4; + coordoff += 4; + } else { /* Handle the case of executeInterleaved 3f */ + stride += 3; + normoff += 3; + coordoff += 3; + } + } + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + if (EXTRA_DEBUGGING) { + System.err.println(" Number of tex coord sets: " + texCoordSetCount); + } + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + texSize = 2; + texStride = 2 * texCoordSetCount; + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + texSize = 3; + texStride = 3 * texCoordSetCount; + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + texSize = 4; + texStride = 4 * texCoordSetCount; + } + stride += texStride; + normoff += texStride; + coloroff += texStride; + coordoff += texStride; + } + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + vAttrStride += vertexAttrSizes[i]; + } + stride += vAttrStride; + normoff += vAttrStride; + coloroff += vAttrStride; + coordoff += vAttrStride; + texCoordoff += vAttrStride; + } + + bstride = stride * Buffers.SIZEOF_FLOAT; + + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) { + sarray = ((GeometryStripArrayRetained) geo).stripVertexCounts; + start_array = ((GeometryStripArrayRetained) geo).stripStartOffsetIndices; + } + + // We have to copy if the data isn't specified using NIO + if (varray != null) { + verts = getVertexArrayBuffer(varray); + } else if (varrayBuffer != null) { + verts = varrayBuffer; + } else { + // This should never happen + throw new AssertionError("Unable to get vertex pointer"); + } + + // using byRef interleaved array and has a separate pointer, then .. + int cstride = stride; + if (carray != null) { + clrs = getColorArrayBuffer(carray); + cstride = 4; + } else { + // FIXME: need to "auto-slice" this buffer later + clrs = verts; + } + + cbstride = cstride * Buffers.SIZEOF_FLOAT; + + // Enable normalize for non-uniform scale (which rescale can't handle) + if (isNonUniformScale) { + gl.glEnable(GL2.GL_NORMALIZE); + } + + int startVertex = stride * startVIndex; + int startClrs = cstride * startVIndex; + if (clrs == verts) { + startClrs += coloroff; + } + + /*** Handle non-indexed strip GeometryArray first *******/ + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) { + if (ignoreVertexColors || (carray != null) || + ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0 && ((texCoordSetMapLen > 1) || + (texCoordSetCount > 1)))) { + useInterleavedArrays = false; + } else { + boolean[] tmp = new boolean[1]; + int[] tmp2 = new int[1]; + testForInterleavedArrays(vformat, tmp, tmp2); + useInterleavedArrays = tmp[0]; + iaFormat = tmp2[0]; + } + if (useInterleavedArrays) { + verts.position(startVertex); + gl.glInterleavedArrays(iaFormat, bstride, verts); + } else { + if ((vformat & GeometryArray.NORMALS) != 0) { + verts.position(startVertex + normoff); + gl.glNormalPointer(GL.GL_FLOAT, bstride, verts); + } + if (!ignoreVertexColors && (vformat & GeometryArray.COLOR) != 0) { + if (EXTRA_DEBUGGING) { + System.err.println(" Doing colors"); + } + clrs.position(startClrs); + if ((vformat & GeometryArray.WITH_ALPHA) != 0 || useAlpha) { + gl.glColorPointer(4, GL.GL_FLOAT, cbstride, clrs); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, cbstride, clrs); + } + } + if ((vformat & GeometryArray.COORDINATES) != 0) { + verts.position(startVertex + coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, bstride, verts); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + executeTexture(texCoordSetMapLen, + texSize, bstride, texCoordoff, + texCoordSetMapOffset, + numActiveTexUnitState, + verts, gl); + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vAttrOffset = startVertex + vAttrOff; + for (int i = 0; i < vertexAttrCount; i++) { + ctx.enableVertexAttrArray(gl, i); + verts.position(vAttrOffset); + ctx.vertexAttrPointer(gl, i, vertexAttrSizes[i], + GL.GL_FLOAT, bstride, verts); + vAttrOffset += vertexAttrSizes[i]; + } + } + } + + switch (geo_type) { + case GeometryRetained.GEO_TYPE_TRI_STRIP_SET: + primType = GL.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_TRI_FAN_SET: + primType = GL.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_LINE_STRIP_SET: + primType = GL.GL_LINE_STRIP; + break; + } + + if (gl.isExtensionAvailable("GL_EXT_multi_draw_arrays")) { + gl.glMultiDrawArrays(primType, start_array, 0, sarray, 0, sarray.length); + } else { + for (int i = 0; i < sarray.length; i++) { + gl.glDrawArrays(primType, start_array[i], sarray[i]); + } + } + } else if ((geo_type == GeometryRetained.GEO_TYPE_QUAD_SET) || + (geo_type == GeometryRetained.GEO_TYPE_TRI_SET) || + (geo_type == GeometryRetained.GEO_TYPE_POINT_SET) || + (geo_type == GeometryRetained.GEO_TYPE_LINE_SET)) { + /******* Handle non-indexed non-striped GeometryArray now *****/ + if (ignoreVertexColors || (carray != null) || + ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0 && ((texCoordSetMapLen > 1) || + (texCoordSetCount > 1)))) { + useInterleavedArrays = false; + } else { + boolean[] tmp = new boolean[1]; + int[] tmp2 = new int[1]; + testForInterleavedArrays(vformat, tmp, tmp2); + useInterleavedArrays = tmp[0]; + iaFormat = tmp2[0]; + } + + if (useInterleavedArrays) { + verts.position(startVertex); + gl.glInterleavedArrays(iaFormat, bstride, verts); + } else { + if (EXTRA_DEBUGGING) { + System.err.println(" startVertex: " + startVertex); + System.err.println(" stride: " + stride); + System.err.println(" bstride: " + bstride); + System.err.println(" normoff: " + normoff); + System.err.println(" coloroff: " + coloroff); + System.err.println(" coordoff: " + coordoff); + System.err.println(" texCoordoff: " + texCoordoff); + } + if ((vformat & GeometryArray.NORMALS) != 0) { + verts.position(startVertex + normoff); + gl.glNormalPointer(GL.GL_FLOAT, bstride, verts); + } + if (!ignoreVertexColors && (vformat & GeometryArray.COLOR) != 0) { + clrs.position(startClrs); + if ((vformat & GeometryArray.WITH_ALPHA) != 0 || useAlpha) { + gl.glColorPointer(4, GL.GL_FLOAT, cbstride, clrs); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, cbstride, clrs); + } + } + if ((vformat & GeometryArray.COORDINATES) != 0) { + verts.position(startVertex + coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, bstride, verts); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + executeTexture(texCoordSetMapLen, + texSize, bstride, texCoordoff, + texCoordSetMapOffset, + numActiveTexUnitState, + verts, gl); + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vAttrOffset = startVertex + vAttrOff; + for (int i = 0; i < vertexAttrCount; i++) { + ctx.enableVertexAttrArray(gl, i); + verts.position(vAttrOffset); + ctx.vertexAttrPointer(gl, i, vertexAttrSizes[i], + GL.GL_FLOAT, bstride, verts); + vAttrOffset += vertexAttrSizes[i]; + } + } + } + switch (geo_type){ + case GeometryRetained.GEO_TYPE_QUAD_SET : gl.glDrawArrays(GL2.GL_QUADS, 0, vcount); break; + case GeometryRetained.GEO_TYPE_TRI_SET : gl.glDrawArrays(GL.GL_TRIANGLES, 0, vcount); break; + case GeometryRetained.GEO_TYPE_POINT_SET: gl.glDrawArrays(GL.GL_POINTS, 0, vcount); break; + case GeometryRetained.GEO_TYPE_LINE_SET : gl.glDrawArrays(GL.GL_LINES, 0, vcount); break; + } + } + + /* clean up if we turned on normalize */ + if (isNonUniformScale) { + gl.glDisable(GL2.GL_NORMALIZE); + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + resetVertexAttrs(gl, ctx, vertexAttrCount); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + resetTexture(gl, ctx); + } + } + + + // glLockArrays() is invoked only for indexed geometry, and the + // vertexCount is guarenteed to be >= 0. + private void lockArray(GL2 gl, int vertexCount) { + if (gl.isExtensionAvailable("GL_EXT_compiled_vertex_array")) { + gl.glLockArraysEXT(0, vertexCount); + } + } + + private void unlockArray(GL2 gl) { + if (gl.isExtensionAvailable("GL_EXT_compiled_vertex_array")) { + gl.glUnlockArraysEXT(); + } + } + + private void + executeGeometryArrayVA(Context absCtx, + GeometryArrayRetained geo, + int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int vcount, + int vformat, + int vdefined, + int initialCoordIndex, FloatBuffer fverts, DoubleBuffer dverts, + int initialColorIndex, FloatBuffer fclrs, ByteBuffer bclrs, + int initialNormalIndex, FloatBuffer norms, + int vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndices, FloatBuffer[] vertexAttrData, + int texCoordMapLength, + int[] texCoordSetMap, + int numActiveTexUnit, + int[] texindices, int texStride, FloatBuffer[] texCoords, + int cdirty, + int[] sarray, + int strip_len, + int[] start_array) { + JoglContext ctx = (JoglContext) absCtx; + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + // Enable normalize for non-uniform scale (which rescale can't handle) + if (isNonUniformScale) { + gl.glEnable(GL2.GL_NORMALIZE); + } + + int coordoff = 3 * initialCoordIndex; + // Define the data pointers + if (floatCoordDefined) { + fverts.position(coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, 0, fverts); + } else if (doubleCoordDefined){ + dverts.position(coordoff); + gl.glVertexPointer(3, GL2.GL_DOUBLE, 0, dverts); + } + + if (floatColorsDefined) { + int coloroff; + int sz; + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + coloroff = 4 * initialColorIndex; + sz = 4; + } else { + coloroff = 3 * initialColorIndex; + sz = 3; + } + fclrs.position(coloroff); + gl.glColorPointer(sz, GL.GL_FLOAT, 0, fclrs); + } else if (byteColorsDefined) { + int coloroff; + int sz; + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + coloroff = 4 * initialColorIndex; + sz = 4; + } else { + coloroff = 3 * initialColorIndex; + sz = 3; + } + bclrs.position(coloroff); + gl.glColorPointer(sz, GL.GL_UNSIGNED_BYTE, 0, bclrs); + } + if (normalsDefined) { + int normoff = 3 * initialNormalIndex; + norms.position(normoff); + gl.glNormalPointer(GL.GL_FLOAT, 0, norms); + } + + if (vattrDefined) { + for (int i = 0; i < vertexAttrCount; i++) { + FloatBuffer vertexAttrs = vertexAttrData[i]; + int sz = vertexAttrSizes[i]; + int initIdx = vertexAttrIndices[i]; + ctx.enableVertexAttrArray(gl, i); + vertexAttrs.position(initIdx * sz); + ctx.vertexAttrPointer(gl, i, sz, GL.GL_FLOAT, 0, vertexAttrs); + } + } + + if (textureDefined) { + int texSet = 0; + for (int i = 0; i < numActiveTexUnit; i++) { + if (( i < texCoordMapLength) && + ((texSet = texCoordSetMap[i]) != -1)) { + FloatBuffer buf = texCoords[texSet]; + buf.position(texStride * texindices[texSet]); + enableTexCoordPointer(gl, i, texStride, + GL.GL_FLOAT, 0, buf); + } else { + disableTexCoordPointer(gl, i); + } + } + + // Reset client active texture unit to 0 + clientActiveTextureUnit(gl, 0); + } + + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) { + int primType = 0; + switch (geo_type) { + case GeometryRetained.GEO_TYPE_TRI_STRIP_SET: + primType = GL.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_TRI_FAN_SET: + primType = GL.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_LINE_STRIP_SET: + primType = GL.GL_LINE_STRIP; + break; + } + if (gl.isExtensionAvailable("GL_EXT_multi_draw_arrays")) { + gl.glMultiDrawArrays(primType, start_array, 0, sarray, 0, strip_len); + } else if (gl.isExtensionAvailable("GL_VERSION_1_4")) { + gl.glMultiDrawArrays(primType, start_array, 0, sarray, 0, strip_len); + } else { + for (int i = 0; i < strip_len; i++) { + gl.glDrawArrays(primType, start_array[i], sarray[i]); + } + } + } else { + switch (geo_type){ + case GeometryRetained.GEO_TYPE_QUAD_SET : gl.glDrawArrays(GL2.GL_QUADS, 0, vcount); break; + case GeometryRetained.GEO_TYPE_TRI_SET : gl.glDrawArrays(GL.GL_TRIANGLES, 0, vcount); break; + case GeometryRetained.GEO_TYPE_POINT_SET : gl.glDrawArrays(GL.GL_POINTS, 0, vcount); break; + case GeometryRetained.GEO_TYPE_LINE_SET : gl.glDrawArrays(GL.GL_LINES, 0, vcount); break; + } + } + + // clean up if we turned on normalize + if (isNonUniformScale) { + gl.glDisable(GL2.GL_NORMALIZE); + } + + if (vattrDefined) { + resetVertexAttrs(gl, ctx, vertexAttrCount); + } + + if (textureDefined) { + resetTexture(gl, ctx); + } + } + + private String getVertexDescription(int vformat) { + String res = ""; + if ((vformat & GeometryArray.COORDINATES) != 0) res += "COORDINATES "; + if ((vformat & GeometryArray.NORMALS) != 0) res += "NORMALS "; + if ((vformat & GeometryArray.COLOR) != 0) res += "COLOR "; + if ((vformat & GeometryArray.WITH_ALPHA) != 0) res += "(WITH_ALPHA) "; + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) res += "TEXTURE_COORDINATE "; + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) res += "(2) "; + if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) res += "(3) "; + if ((vformat & GeometryArray.TEXTURE_COORDINATE_4) != 0) res += "(4) "; + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) res += "VERTEX_ATTRIBUTES "; + return res; + } + + private String getGeometryDescription(int geo_type) { + switch (geo_type) { + case GeometryRetained.GEO_TYPE_TRI_STRIP_SET : return "GEO_TYPE_TRI_STRIP_SET"; + case GeometryRetained.GEO_TYPE_TRI_FAN_SET : return "GEO_TYPE_TRI_FAN_SET"; + case GeometryRetained.GEO_TYPE_LINE_STRIP_SET: return "GEO_TYPE_LINE_STRIP_SET"; + case GeometryRetained.GEO_TYPE_QUAD_SET : return "GEO_TYPE_QUAD_SET"; + case GeometryRetained.GEO_TYPE_TRI_SET : return "GEO_TYPE_TRI_SET"; + case GeometryRetained.GEO_TYPE_POINT_SET : return "GEO_TYPE_POINT_SET"; + case GeometryRetained.GEO_TYPE_LINE_SET : return "GEO_TYPE_LINE_SET"; + default: return "(unknown " + geo_type + ")"; + } + } + + private void resetVertexAttrs(GL gl, JoglContext ctx, int vertexAttrCount) { + // Disable specified vertex attr arrays + for (int i = 0; i < vertexAttrCount; i++) { + ctx.disableVertexAttrArray(gl, i); + } + } + + + // --------------------------------------------------------------------- + + // + // IndexedGeometryArrayRetained methods + // + + // by-copy or interleaved, by reference, Java arrays + @Override + void executeIndexedGeometry(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + float[] varray, float[] carray, + int cdirty, + int[] indexCoord) { + if (VERBOSE) System.err.println("JoglPipeline.executeIndexedGeometry()"); + + executeIndexedGeometryArray(ctx, geo, geo_type, + isNonUniformScale, useAlpha, ignoreVertexColors, + initialIndexIndex, indexCount, + vertexCount, vformat, + vertexAttrCount, vertexAttrSizes, + texCoordSetCount, texCoordSetMap, texCoordSetMapLen, + texCoordSetOffset, + numActiveTexUnitState, + varray, null, carray, + cdirty, indexCoord); + } + + // interleaved, by reference, nio buffer + @Override + void executeIndexedGeometryBuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + FloatBuffer vdata, float[] carray, + int cDirty, + int[] indexCoord) { + if (VERBOSE) System.err.println("JoglPipeline.executeIndexedGeometryBuffer()"); + + executeIndexedGeometryArray(ctx, geo, geo_type, + isNonUniformScale, useAlpha, ignoreVertexColors, + initialIndexIndex, indexCount, vertexCount, vformat, + 0, null, + texCoordSetCount, texCoordSetMap, texCoordSetMapLen, texCoordSetOffset, + numActiveTexUnitState, + null, vdata, carray, + cDirty, indexCoord); + } + + // non interleaved, by reference, Java arrays + @Override + void executeIndexedGeometryVA(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vdefined, + float[] vfcoords, double[] vdcoords, + float[] cfdata, byte[] cbdata, + float[] ndata, + int vertexAttrCount, int[] vertexAttrSizes, + float[][] vertexAttrData, + int texCoordMapLength, + int[] texcoordoffset, + int numActiveTexUnitState, + int texStride, Object[] texCoords, + int cdirty, + int[] indexCoord) { + if (VERBOSE) System.err.println("JoglPipeline.executeIndexedGeometryVA()"); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + FloatBuffer[] texCoordBufs = null; + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) { + vertexAttrBufs = getVertexAttrSetBuffer(vertexAttrData); + } + + // get texture arrays + if (textureDefined) { + texCoordBufs = getTexCoordSetBuffer(texCoords); + } + + int[] sarray = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + sarray = ((IndexedGeometryStripArrayRetained) geo).stripIndexCounts; + strip_len = sarray.length; + } + + // get coordinate array + if (floatCoordDefined) { + fverts = getVertexArrayBuffer(vfcoords); + } else if (doubleCoordDefined) { + dverts = getVertexArrayBuffer(vdcoords); + } + + // get color array + if (floatColorsDefined) { + fclrs = getColorArrayBuffer(cfdata); + } else if (byteColorsDefined) { + bclrs = getColorArrayBuffer(cbdata); + } + + // get normal array + if (normalsDefined) { + norms = getNormalArrayBuffer(ndata); + } + + executeIndexedGeometryArrayVA(ctx, geo, geo_type, + isNonUniformScale, ignoreVertexColors, + initialIndexIndex, validIndexCount, vertexCount, + vformat, vdefined, + fverts, dverts, + fclrs, bclrs, + norms, + vertexAttrCount, vertexAttrSizes, vertexAttrBufs, + texCoordMapLength, texcoordoffset, + numActiveTexUnitState, + texStride, texCoordBufs, + cdirty, indexCoord, + sarray, strip_len); + } + + // non interleaved, by reference, nio buffer + @Override + void executeIndexedGeometryVABuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vdefined, + Buffer vcoords, + Buffer cdataBuffer, + float[] cfdata, byte[] cbdata, + FloatBuffer ndata, + int vertexAttrCount, int[] vertexAttrSizes, + FloatBuffer[] vertexAttrData, + int texCoordMapLength, + int[] texcoordoffset, + int numActiveTexUnitState, + int texStride, Object[] texCoords, + int cdirty, + int[] indexCoord) { + if (VERBOSE) System.err.println("JoglPipeline.executeIndexedGeometryVABuffer()"); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + FloatBuffer[] texCoordBufs = null; + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) { + vertexAttrBufs = vertexAttrData; + } + + // get texture arrays + if (textureDefined) { + texCoordBufs = new FloatBuffer[texCoords.length]; + for (int i = 0; i < texCoords.length; i++) { + texCoordBufs[i] = (FloatBuffer) texCoords[i]; + } + } + + // get coordinate array + if (floatCoordDefined) { + fverts = (FloatBuffer) vcoords; + } else if (doubleCoordDefined) { + dverts = (DoubleBuffer) vcoords; + } + + if (fverts == null && dverts == null) { + return; + } + + int[] sarray = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + sarray = ((IndexedGeometryStripArrayRetained) geo).stripIndexCounts; + strip_len = sarray.length; + } + + // get color array + if (floatColorsDefined) { + if (cfdata != null) + fclrs = getColorArrayBuffer(cfdata); + else + fclrs = (FloatBuffer) cdataBuffer; + } else if (byteColorsDefined) { + if (cbdata != null) + bclrs = getColorArrayBuffer(cbdata); + else + bclrs = (ByteBuffer) cdataBuffer; + } + + // get normal array + if (normalsDefined) { + norms = ndata; + } + + executeIndexedGeometryArrayVA(ctx, geo, geo_type, + isNonUniformScale, ignoreVertexColors, + initialIndexIndex, validIndexCount, vertexCount, + vformat, vdefined, + fverts, dverts, + fclrs, bclrs, + norms, + vertexAttrCount, vertexAttrSizes, vertexAttrBufs, + texCoordMapLength, texcoordoffset, + numActiveTexUnitState, + texStride, texCoordBufs, + cdirty, indexCoord, + sarray, strip_len); + } + + // by-copy geometry + @Override + void buildIndexedGeometry(Context absCtx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, boolean updateAlpha, + float alpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetMapOffset, + double[] xform, double[] nxform, + float[] varray, int[] indexCoord) { + if (VERBOSE) System.err.println("JoglPipeline.buildIndexedGeometry()"); + + JoglContext ctx = (JoglContext) absCtx; + GL2 gl = context(ctx).getGL().getGL2(); + + boolean useInterleavedArrays; + int iaFormat = 0; + int primType = 0; + int stride = 0, coordoff = 0, normoff = 0, coloroff = 0, texCoordoff = 0; + int texSize = 0, texStride = 0; + int vAttrOff = 0; + int vAttrStride = 0; + int bstride = 0, cbstride = 0; + FloatBuffer verts = null; + FloatBuffer clrs = null; + int[] sarray = null; + int strip_len = 0; + boolean useAlpha = false; + + if ((vformat & GeometryArray.COORDINATES) != 0) { + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + stride += 3; + } else { + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); + } + + if ((vformat & GeometryArray.NORMALS) != 0) { + gl.glEnableClientState(GL2.GL_NORMAL_ARRAY); + stride += 3; + coordoff += 3; + } else { + gl.glDisableClientState(GL2.GL_NORMAL_ARRAY); + } + + if ((vformat & GeometryArray.COLOR) != 0) { + gl.glEnableClientState(GL2.GL_COLOR_ARRAY); + stride += 4; + normoff += 4; + coordoff += 4; + } else { + gl.glDisableClientState(GL2.GL_COLOR_ARRAY); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + texSize = 2; + texStride = 2 * texCoordSetCount; + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + texSize = 3; + texStride = 3 * texCoordSetCount; + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + texSize = 4; + texStride = 4 * texCoordSetCount; + } + stride += texStride; + normoff += texStride; + coloroff += texStride; + coordoff += texStride; + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + vAttrStride += vertexAttrSizes[i]; + } + stride += vAttrStride; + normoff += vAttrStride; + coloroff += vAttrStride; + coordoff += vAttrStride; + texCoordoff += vAttrStride; + } + + bstride = stride * Buffers.SIZEOF_FLOAT; + + // process alpha for geometryArray without alpha + if (updateAlpha && !ignoreVertexColors) { + useAlpha = true; + } + + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + sarray = ((IndexedGeometryStripArrayRetained) geo).stripIndexCounts; + strip_len = sarray.length; + } + + // Copy data into NIO array + verts = getVertexArrayBuffer(varray); + + // Apply normal transform if necessary + if ((vformat & GeometryArray.NORMALS) != 0 && nxform != null) { + int off = normoff; + for (int i = 0; i < vertexCount * 3; i+=3) { + verts.put(off , (float) (nxform[0] * varray[off] + + nxform[1] * varray[off+1] + + nxform[2] * varray[off+2])); + verts.put(off+1, (float) (nxform[4] * varray[off] + + nxform[5] * varray[off+1] + + nxform[6] * varray[off+2])); + verts.put(off+2, (float) (nxform[8] * varray[off] + + nxform[9] * varray[off+1] + + nxform[10] * varray[off+2])); + off += stride; + } + } + + // Apply coordinate transform if necessary + if ((vformat & GeometryArray.COORDINATES) != 0 && xform != null) { + int off = coordoff; + for (int i = 0; i < vertexCount * 3; i+=3) { + verts.put(off , (float) (xform[0] * varray[off] + + xform[1] * varray[off+1] + + xform[2] * varray[off+2])); + verts.put(off+1, (float) (xform[4] * varray[off] + + xform[5] * varray[off+1] + + xform[6] * varray[off+2])); + verts.put(off+2, (float) (xform[8] * varray[off] + + xform[9] * varray[off+1] + + xform[10] * varray[off+2])); + off += stride; + } + } + + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + // Note we can use interleaved arrays even if we have a + // non-null xform since we use the same data layout unlike the + // C code + if (ignoreVertexColors || + (((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) && ((texCoordSetMapLen > 1) || + (texCoordSetCount > 1)))) { + useInterleavedArrays = false; + } else { + boolean[] tmp = new boolean[1]; + int[] tmp2 = new int[1]; + testForInterleavedArrays(vformat, tmp, tmp2); + useInterleavedArrays = tmp[0]; + iaFormat = tmp2[0]; + } + + if (useInterleavedArrays) { + verts.position(0); + gl.glInterleavedArrays(iaFormat, bstride, verts); + } else { + if ((vformat & GeometryArray.NORMALS) != 0) { + verts.position(normoff); + gl.glNormalPointer(GL.GL_FLOAT, bstride, verts); + } + if (!ignoreVertexColors && ((vformat & GeometryArray.COLOR) != 0)) { + verts.position(coloroff); + if (((vformat & GeometryArray.WITH_ALPHA) != 0) || useAlpha) { + gl.glColorPointer(4, GL.GL_FLOAT, bstride, verts); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, bstride, verts); + } + } + if ((vformat & GeometryArray.COORDINATES) != 0) { + verts.position(coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, bstride, verts); + } + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + executeTexture(texCoordSetMapLen, + texSize, bstride, texCoordoff, + texCoordSetMapOffset, + texCoordSetMapLen, + verts, gl); + } + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vAttrOffset = vAttrOff; + for (int i = 0; i < vertexAttrCount; i++) { + ctx.enableVertexAttrArray(gl, i); + verts.position(vAttrOffset); + ctx.vertexAttrPointer(gl, i, vertexAttrSizes[i], + GL.GL_FLOAT, bstride, verts); + vAttrOffset += vertexAttrSizes[i]; + } + } + } + + switch (geo_type) { + case GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET: + primType = GL.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET: + primType = GL.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET: + primType = GL.GL_LINE_STRIP; + break; + } + + lockArray(gl, vertexCount); + + // Note: using MultiDrawElements is probably more expensive than + // not in this case due to the need to allocate more temporary + // direct buffers and slice up the incoming indices array + int offset = initialIndexIndex; + IntBuffer indicesBuffer = IntBuffer.wrap(indexCoord); + for (int i = 0; i < strip_len; i++) { + indicesBuffer.position(offset); + int count = sarray[i]; + gl.glDrawElements(primType, count, GL.GL_UNSIGNED_INT, indicesBuffer); + offset += count; + } + } else if ((geo_type == GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET) || + (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_SET) || + (geo_type == GeometryRetained.GEO_TYPE_INDEXED_POINT_SET) || + (geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_SET)) { + // Note we can use interleaved arrays even if we have a + // non-null xform since we use the same data layout unlike the + // C code + if (ignoreVertexColors || + (((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) && ((texCoordSetMapLen > 1) || + (texCoordSetCount > 1)))) { + useInterleavedArrays = false; + } else { + boolean[] tmp = new boolean[1]; + int[] tmp2 = new int[1]; + testForInterleavedArrays(vformat, tmp, tmp2); + useInterleavedArrays = tmp[0]; + iaFormat = tmp2[0]; + } + + if (useInterleavedArrays) { + verts.position(0); + gl.glInterleavedArrays(iaFormat, bstride, verts); + } else { + if ((vformat & GeometryArray.NORMALS) != 0) { + verts.position(normoff); + gl.glNormalPointer(GL.GL_FLOAT, bstride, verts); + } + + if (!ignoreVertexColors && ((vformat & GeometryArray.COLOR) != 0)) { + verts.position(coloroff); + if (((vformat & GeometryArray.WITH_ALPHA) != 0) || useAlpha) { + gl.glColorPointer(4, GL.GL_FLOAT, bstride, verts); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, bstride, verts); + } + } + if ((vformat & GeometryArray.COORDINATES) != 0) { + verts.position(coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, bstride, verts); + } + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + executeTexture(texCoordSetMapLen, + texSize, bstride, texCoordoff, + texCoordSetMapOffset, + texCoordSetMapLen, + verts, gl); + } + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vAttrOffset = vAttrOff; + for (int i = 0; i < vertexAttrCount; i++) { + ctx.enableVertexAttrArray(gl, i); + verts.position(vAttrOffset); + ctx.vertexAttrPointer(gl, i, vertexAttrSizes[i], + GL.GL_FLOAT, bstride, verts); + vAttrOffset += vertexAttrSizes[i]; + } + } + + switch (geo_type) { + case GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET : + primType = GL2.GL_QUADS; + break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_SET : + primType = GL.GL_TRIANGLES; + break; + case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET : + primType = GL.GL_POINTS; + break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET : + primType = GL.GL_LINES; + break; + } + + lockArray(gl, vertexCount); + + IntBuffer indicesBuffer = IntBuffer.wrap(indexCoord); + indicesBuffer.position(initialIndexIndex); + gl.glDrawElements(primType, validIndexCount, GL.GL_UNSIGNED_INT, indicesBuffer); + } + } + + unlockArray(gl); + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + resetVertexAttrs(gl, ctx, vertexAttrCount); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + resetTexture(gl, ctx); + } + } + + + //---------------------------------------------------------------------- + // + // Helper routines for IndexedGeometryArrayRetained + // + + private void executeIndexedGeometryArray(Context absCtx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + float[] varray, FloatBuffer vdata, float[] carray, + int cDirty, + int[] indexCoord) { + JoglContext ctx = (JoglContext) absCtx; + GL2 gl = context(ctx).getGL().getGL2(); + + boolean useInterleavedArrays; + int iaFormat = 0; + int primType = 0; + int stride = 0, coordoff = 0, normoff = 0, coloroff = 0, texCoordoff = 0; + int texSize = 0, texStride = 0; + int vAttrOff = 0; + int vAttrStride = 0; + int bstride = 0, cbstride = 0; + FloatBuffer verts = null; + FloatBuffer clrs = null; + int[] sarray = null; + int strip_len = 0; + + if ((vformat & GeometryArray.COORDINATES) != 0) { + stride += 3; + } + if ((vformat & GeometryArray.NORMALS) != 0) { + stride += 3; + coordoff += 3; + } + + if ((vformat & GeometryArray.COLOR) != 0) { + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + stride += 4; + normoff += 4; + coordoff += 4; + } else { // Handle the case of executeInterleaved 3f + stride += 3; + normoff += 3; + coordoff += 3; + } + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + texSize = 2; + texStride = 2 * texCoordSetCount; + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + texSize = 3; + texStride = 3 * texCoordSetCount; + } else if ((vformat & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + texSize = 4; + texStride = 4 * texCoordSetCount; + } + stride += texStride; + normoff += texStride; + coloroff += texStride; + coordoff += texStride; + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + for (int i = 0; i < vertexAttrCount; i++) { + vAttrStride += vertexAttrSizes[i]; + } + stride += vAttrStride; + normoff += vAttrStride; + coloroff += vAttrStride; + coordoff += vAttrStride; + texCoordoff += vAttrStride; + } + + bstride = stride * Buffers.SIZEOF_FLOAT; + + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + sarray = ((IndexedGeometryStripArrayRetained) geo).stripIndexCounts; + strip_len = sarray.length; + } + + // We have to copy if the data isn't specified using NIO + if (varray != null) { + verts = getVertexArrayBuffer(varray); + } else if (vdata != null) { + verts = vdata; + } else { + // This should never happen + throw new AssertionError("Unable to get vertex pointer"); + } + + // using byRef interleaved array and has a separate pointer, then .. + int cstride = stride; + if (carray != null) { + clrs = getColorArrayBuffer(carray); + cstride = 4; + } else { + // FIXME: need to "auto-slice" this buffer later + clrs = verts; + } + + cbstride = cstride * Buffers.SIZEOF_FLOAT; + + // Enable normalize for non-uniform scale (which rescale can't handle) + if (isNonUniformScale) { + gl.glEnable(GL2.GL_NORMALIZE); + } + + /*** Handle non-indexed strip GeometryArray first *******/ + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + if (ignoreVertexColors || (carray != null) || + ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0 && ((texCoordSetMapLen > 1) || + (texCoordSetCount > 1)))) { + useInterleavedArrays = false; + } else { + boolean[] tmp = new boolean[1]; + int[] tmp2 = new int[1]; + testForInterleavedArrays(vformat, tmp, tmp2); + useInterleavedArrays = tmp[0]; + iaFormat = tmp2[0]; + } + if (useInterleavedArrays) { + verts.position(0); + gl.glInterleavedArrays(iaFormat, bstride, verts); + } else { + if ((vformat & GeometryArray.NORMALS) != 0) { + verts.position(normoff); + gl.glNormalPointer(GL.GL_FLOAT, bstride, verts); + } + if (!ignoreVertexColors && (vformat & GeometryArray.COLOR) != 0) { + if (clrs == verts) { + clrs.position(coloroff); + } + if ((vformat & GeometryArray.WITH_ALPHA) != 0 || useAlpha) { + gl.glColorPointer(4, GL.GL_FLOAT, cbstride, clrs); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, cbstride, clrs); + } + } + if ((vformat & GeometryArray.COORDINATES) != 0) { + verts.position(coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, bstride, verts); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + /* XXXX: texCoordoff == 0 ???*/ + executeTexture(texCoordSetMapLen, + texSize, bstride, texCoordoff, + texCoordSetOffset, + numActiveTexUnitState, + verts, gl); + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vAttrOffset = vAttrOff; + for (int i = 0; i < vertexAttrCount; i++) { + ctx.enableVertexAttrArray(gl, i); + verts.position(vAttrOffset); + ctx.vertexAttrPointer(gl, i, vertexAttrSizes[i], + GL.GL_FLOAT, bstride, verts); + vAttrOffset += vertexAttrSizes[i]; + } + } + } + + switch (geo_type) { + case GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET: + primType = GL.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET: + primType = GL.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET: + primType = GL.GL_LINE_STRIP; + break; + } + + lockArray(gl, vertexCount); + + // Note: using MultiDrawElements is probably more expensive than + // not in this case due to the need to allocate more temporary + // direct buffers and slice up the incoming indices array + int offset = initialIndexIndex; + IntBuffer indicesBuffer = IntBuffer.wrap(indexCoord); + for (int i = 0; i < strip_len; i++) { + indicesBuffer.position(offset); + int count = sarray[i]; + gl.glDrawElements(primType, count, GL.GL_UNSIGNED_INT, indicesBuffer); + offset += count; + } + } else if ((geo_type == GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET) || + (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_SET) || + (geo_type == GeometryRetained.GEO_TYPE_INDEXED_POINT_SET) || + (geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_SET)) { + /******* Handle non-indexed non-striped GeometryArray now *****/ + if (ignoreVertexColors || (carray != null) || + ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0 && ((texCoordSetMapLen > 1) || + (texCoordSetCount > 1)))) { + useInterleavedArrays = false; + } else { + boolean[] tmp = new boolean[1]; + int[] tmp2 = new int[1]; + testForInterleavedArrays(vformat, tmp, tmp2); + useInterleavedArrays = tmp[0]; + iaFormat = tmp2[0]; + } + + if (useInterleavedArrays) { + verts.position(0); + gl.glInterleavedArrays(iaFormat, bstride, verts); + } else { + if ((vformat & GeometryArray.NORMALS) != 0) { + verts.position(normoff); + gl.glNormalPointer(GL.GL_FLOAT, bstride, verts); + } + + if (!ignoreVertexColors && (vformat & GeometryArray.COLOR) != 0) { + if (clrs == verts) { + clrs.position(coloroff); + } + if ((vformat & GeometryArray.WITH_ALPHA) != 0 || useAlpha) { + gl.glColorPointer(4, GL.GL_FLOAT, cbstride, clrs); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, cbstride, clrs); + } + } + if ((vformat & GeometryArray.COORDINATES) != 0) { + verts.position(coordoff); + gl.glVertexPointer(3, GL.GL_FLOAT, bstride, verts); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + /* XXXX: texCoordoff == 0 ???*/ + executeTexture(texCoordSetMapLen, + texSize, bstride, texCoordoff, + texCoordSetOffset, + numActiveTexUnitState, + verts, gl); + } + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + int vAttrOffset = vAttrOff; + for (int i = 0; i < vertexAttrCount; i++) { + ctx.enableVertexAttrArray(gl, i); + verts.position(vAttrOffset); + ctx.vertexAttrPointer(gl, i, vertexAttrSizes[i], + GL.GL_FLOAT, bstride, verts); + vAttrOffset += vertexAttrSizes[i]; + } + } + } + + lockArray(gl, vertexCount); + IntBuffer buf = IntBuffer.wrap(indexCoord); + buf.position(initialIndexIndex); + switch (geo_type){ + case GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET : gl.glDrawElements(GL2.GL_QUADS, indexCount, GL.GL_UNSIGNED_INT, buf); break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_SET : gl.glDrawElements(GL.GL_TRIANGLES, indexCount, GL.GL_UNSIGNED_INT, buf); break; + case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET: gl.glDrawElements(GL.GL_POINTS, indexCount, GL.GL_UNSIGNED_INT, buf); break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET : gl.glDrawElements(GL.GL_LINES, indexCount, GL.GL_UNSIGNED_INT, buf); break; + } + } + + unlockArray(gl); + + if ((vformat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + resetVertexAttrs(gl, ctx, vertexAttrCount); + } + + if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) { + resetTexture(gl, ctx); + } + + // clean up if we turned on normalize + if (isNonUniformScale) { + gl.glDisable(GL2.GL_NORMALIZE); + } + } + + + private void executeIndexedGeometryArrayVA(Context absCtx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, int vformat, int vdefined, + FloatBuffer fverts, DoubleBuffer dverts, + FloatBuffer fclrs, ByteBuffer bclrs, + FloatBuffer norms, + int vertexAttrCount, int[] vertexAttrSizes, FloatBuffer[] vertexAttrBufs, + int texCoordSetCount, int[] texCoordSetMap, + int numActiveTexUnitState, + int texStride, + FloatBuffer[] texCoords, + int cDirty, int[] indexCoord, int[] sarray, int strip_len) { + JoglContext ctx = (JoglContext) absCtx; + GL2 gl = context(ctx).getGL().getGL2(); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + // Enable normalize for non-uniform scale (which rescale can't handle) + if (isNonUniformScale) { + gl.glEnable(GL2.GL_NORMALIZE); + } + + // Define the data pointers + if (floatCoordDefined) { + fverts.position(0); + gl.glVertexPointer(3, GL.GL_FLOAT, 0, fverts); + } else if (doubleCoordDefined){ + dverts.position(0); + gl.glVertexPointer(3, GL2.GL_DOUBLE, 0, dverts); + } + if (floatColorsDefined) { + fclrs.position(0); + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + gl.glColorPointer(4, GL.GL_FLOAT, 0, fclrs); + } else { + gl.glColorPointer(3, GL.GL_FLOAT, 0, fclrs); + } + } else if (byteColorsDefined) { + bclrs.position(0); + if ((vformat & GeometryArray.WITH_ALPHA) != 0) { + gl.glColorPointer(4, GL.GL_UNSIGNED_BYTE, 0, bclrs); + } else { + gl.glColorPointer(3, GL.GL_UNSIGNED_BYTE, 0, bclrs); + } + } + if (normalsDefined) { + norms.position(0); + gl.glNormalPointer(GL.GL_FLOAT, 0, norms); + } + + if (vattrDefined) { + for (int i = 0; i < vertexAttrCount; i++) { + FloatBuffer vertexAttrs = vertexAttrBufs[i]; + int sz = vertexAttrSizes[i]; + ctx.enableVertexAttrArray(gl, i); + vertexAttrs.position(0); + ctx.vertexAttrPointer(gl, i, sz, GL.GL_FLOAT, 0, vertexAttrs); + } + } + + if (textureDefined) { + int texSet = 0; + for (int i = 0; i < numActiveTexUnitState; i++) { + if ((i < texCoordSetCount) && + ((texSet = texCoordSetMap[i]) != -1)) { + FloatBuffer buf = texCoords[texSet]; + buf.position(0); + enableTexCoordPointer(gl, i, texStride, + GL.GL_FLOAT, 0, buf); + } else { + disableTexCoordPointer(gl, i); + } + } + + // Reset client active texture unit to 0 + clientActiveTextureUnit(gl, 0); + } + + lockArray(gl, vertexCount); + + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET || + geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) { + int primType = 0; + switch (geo_type) { + case GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET: + primType = GL.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET: + primType = GL.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET: + primType = GL.GL_LINE_STRIP; + break; + } + + // Note: using MultiDrawElements is probably more expensive than + // not in this case due to the need to allocate more temporary + // direct buffers and slice up the incoming indices array + int offset = initialIndexIndex; + IntBuffer indicesBuffer = IntBuffer.wrap(indexCoord); + for (int i = 0; i < strip_len; i++) { + indicesBuffer.position(offset); + int count = sarray[i]; + gl.glDrawElements(primType, count, GL.GL_UNSIGNED_INT, indicesBuffer); + offset += count; + } + } else { + IntBuffer buf = IntBuffer.wrap(indexCoord); + buf.position(initialIndexIndex); + switch (geo_type){ + case GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET : gl.glDrawElements(GL2.GL_QUADS, validIndexCount, GL.GL_UNSIGNED_INT, buf); break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_SET : gl.glDrawElements(GL.GL_TRIANGLES, validIndexCount, GL.GL_UNSIGNED_INT, buf); break; + case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET: gl.glDrawElements(GL.GL_POINTS, validIndexCount, GL.GL_UNSIGNED_INT, buf); break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET : gl.glDrawElements(GL.GL_LINES, validIndexCount, GL.GL_UNSIGNED_INT, buf); break; + } + } + + unlockArray(gl); + + // clean up if we turned on normalize + if (isNonUniformScale) { + gl.glDisable(GL2.GL_NORMALIZE); + } + + if (vattrDefined) { + resetVertexAttrs(gl, ctx, vertexAttrCount); + } + + if (textureDefined) { + resetTexture(gl, ctx); + } + } + + + // --------------------------------------------------------------------- + + // + // GraphicsContext3D methods + // + + // Native method for readRaster + @Override + void readRaster(Context ctx, + int type, int xSrcOffset, int ySrcOffset, + int width, int height, int hCanvas, + int imageDataType, + int imageFormat, + Object imageBuffer, + int depthFormat, + Object depthBuffer) { + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, width); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); + int yAdjusted = hCanvas - height - ySrcOffset; + + if ((type & Raster.RASTER_COLOR) != 0) { + int oglFormat = 0; + if(imageDataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) { + + switch (imageFormat) { + case ImageComponentRetained.TYPE_BYTE_BGR: + oglFormat = GL2.GL_BGR; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + oglFormat = GL.GL_RGB; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) { // If its zero, should never come here! + oglFormat = GL2.GL_ABGR_EXT; + } else { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + oglFormat = GL.GL_RGBA; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + oglFormat = GL.GL_LUMINANCE_ALPHA; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + gl.glReadPixels(xSrcOffset, yAdjusted, width, height, + oglFormat, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap((byte[]) imageBuffer)); + + + } else if(imageDataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) { + int intType = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + + switch (imageFormat) { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + oglFormat = GL.GL_RGBA; + intType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + oglFormat = GL2.GL_BGRA; + intType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* This method only supports 3 and 4 components formats and INT types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + + /* Force Alpha to 1.0 if needed */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + } + + gl.glReadPixels(xSrcOffset, yAdjusted, width, height, + oglFormat, intType, IntBuffer.wrap((int[]) imageBuffer)); + + /* Restore Alpha scale and bias */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + } + + } else { + assert false; + } + } + + if ((type & Raster.RASTER_DEPTH) != 0) { + + if (depthFormat == DepthComponentRetained.DEPTH_COMPONENT_TYPE_INT) { + // yOffset is adjusted for OpenGL - Y upward + gl.glReadPixels(xSrcOffset, yAdjusted, width, height, + GL2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, IntBuffer.wrap((int[]) depthBuffer)); + } else { + // DEPTH_COMPONENT_TYPE_FLOAT + // yOffset is adjusted for OpenGL - Y upward + gl.glReadPixels(xSrcOffset, yAdjusted, width, height, + GL2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, FloatBuffer.wrap((float[]) depthBuffer)); + } + } + + } + + // --------------------------------------------------------------------- + + // + // GLSLShaderProgramRetained methods + // + + // ShaderAttributeValue methods + + @Override + ShaderError setGLSLUniform1i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform1i()"); + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform1iARB(unbox(uniformLocation), value); + return null; + } + + @Override + ShaderError setGLSLUniform1f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform1f()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform1fARB(unbox(uniformLocation), value); + return null; + } + + @Override + ShaderError setGLSLUniform2i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform2i()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform2iARB(unbox(uniformLocation), value[0], value[1]); + return null; + } + + @Override + ShaderError setGLSLUniform2f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform2f()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform2fARB(unbox(uniformLocation), value[0], value[1]); + return null; + } + + @Override + ShaderError setGLSLUniform3i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform3i()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform3iARB(unbox(uniformLocation), value[0], value[1], value[2]); + return null; + } + + @Override + ShaderError setGLSLUniform3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform3f()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform3fARB(unbox(uniformLocation), value[0], value[1], value[2]); + return null; + } + + @Override + ShaderError setGLSLUniform4i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform4i()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform4iARB(unbox(uniformLocation), value[0], value[1], value[2], value[3]); + return null; + } + + @Override + ShaderError setGLSLUniform4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform4f()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform4fARB(unbox(uniformLocation), value[0], value[1], value[2], value[3]); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniformMatrix3f()"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniformMatrix3fvARB(unbox(uniformLocation), 1, true, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniformMatrix4f()"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniformMatrix4fvARB(unbox(uniformLocation), 1, true, value, 0); + return null; + } + + // ShaderAttributeArray methods + + @Override + ShaderError setGLSLUniform1iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform1iArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform1ivARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform1fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform1fArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform1fvARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform2iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform2iArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform2ivARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform2fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform2fArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform2fvARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform3iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform3iArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform3ivARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform3fArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform3fvARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform4iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform4iArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform4ivARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniform4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniform4fArray()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniform4fvARB(unbox(uniformLocation), numElements, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniformMatrix3fArray()"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniformMatrix3fvARB(unbox(uniformLocation), numElements, true, value, 0); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + if (VERBOSE) System.err.println("JoglPipeline.setGLSLUniformMatrix4fArray()"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUniformMatrix4fvARB(unbox(uniformLocation), numElements, true, value, 0); + return null; + } + + // interfaces for shader compilation, etc. + @Override + ShaderError createGLSLShader(Context ctx, int shaderType, ShaderId[] shaderId) { + if (VERBOSE) System.err.println("JoglPipeline.createGLSLShader()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int shaderHandle = 0; + if (shaderType == Shader.SHADER_TYPE_VERTEX) { + shaderHandle = (int) gl.glCreateShaderObjectARB(GL2.GL_VERTEX_SHADER); + } else if (shaderType == Shader.SHADER_TYPE_FRAGMENT) { + shaderHandle = (int) gl.glCreateShaderObjectARB(GL2.GL_FRAGMENT_SHADER); + } + + if (shaderHandle == 0) { + return new ShaderError(ShaderError.COMPILE_ERROR, + "Unable to create native shader object"); + } + + shaderId[0] = new JoglShaderObject(shaderHandle); + return null; + } + @Override + ShaderError destroyGLSLShader(Context ctx, ShaderId shaderId) { + if (VERBOSE) System.err.println("JoglPipeline.destroyGLSLShader()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glDeleteObjectARB(unbox(shaderId)); + return null; + } + @Override + ShaderError compileGLSLShader(Context ctx, ShaderId shaderId, String program) { + if (VERBOSE) System.err.println("JoglPipeline.compileGLSLShader()"); + + int id = unbox(shaderId); + if (id == 0) { + throw new AssertionError("shaderId == 0"); + } + + if (program == null) { + throw new AssertionError("shader program string is null"); + } + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glShaderSourceARB(id, 1, new String[] { program }, null, 0); + gl.glCompileShaderARB(id); + int[] status = new int[1]; + gl.glGetObjectParameterivARB(id, GL2.GL_OBJECT_COMPILE_STATUS_ARB, status, 0); + if (status[0] == 0) { + String detailMsg = getInfoLog(gl, id); + ShaderError res = new ShaderError(ShaderError.COMPILE_ERROR, + "GLSL shader compile error"); + res.setDetailMessage(detailMsg); + return res; + } + return null; + } + + @Override + ShaderError createGLSLShaderProgram(Context ctx, ShaderProgramId[] shaderProgramId) { + if (VERBOSE) System.err.println("JoglPipeline.createGLSLShaderProgram()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int shaderProgramHandle = (int) gl.glCreateProgramObjectARB(); + if (shaderProgramHandle == 0) { + return new ShaderError(ShaderError.LINK_ERROR, + "Unable to create native shader program object"); + } + shaderProgramId[0] = new JoglShaderObject(shaderProgramHandle); + return null; + } + @Override + ShaderError destroyGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId) { + if (VERBOSE) System.err.println("JoglPipeline.destroyGLSLShaderProgram()"); + GL2 gl = context(ctx).getGL().getGL2(); + gl.glDeleteObjectARB(unbox(shaderProgramId)); + return null; + } + @Override + ShaderError linkGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId, + ShaderId[] shaderIds) { + if (VERBOSE) System.err.println("JoglPipeline.linkGLSLShaderProgram()"); + + GL2 gl = context(ctx).getGL().getGL2(); + int id = unbox(shaderProgramId); + for (int i = 0; i < shaderIds.length; i++) { + gl.glAttachObjectARB(id, unbox(shaderIds[i])); + } + gl.glLinkProgramARB(id); + int[] status = new int[1]; + gl.glGetObjectParameterivARB(id, GL2.GL_OBJECT_LINK_STATUS_ARB, status, 0); + if (status[0] == 0) { + String detailMsg = getInfoLog(gl, id); + ShaderError res = new ShaderError(ShaderError.LINK_ERROR, + "GLSL shader program link error"); + res.setDetailMessage(detailMsg); + return res; + } + return null; + } + @Override + ShaderError bindGLSLVertexAttrName(Context ctx, ShaderProgramId shaderProgramId, + String attrName, int attrIndex) { + if (VERBOSE) System.err.println("JoglPipeline.bindGLSLVertexAttrName()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glBindAttribLocation(unbox(shaderProgramId), + attrIndex + VirtualUniverse.mc.glslVertexAttrOffset, + attrName); + return null; + } + @Override + void lookupGLSLShaderAttrNames(Context ctx, ShaderProgramId shaderProgramId, + int numAttrNames, String[] attrNames, ShaderAttrLoc[] locArr, + int[] typeArr, int[] sizeArr, boolean[] isArrayArr) { + if (VERBOSE) System.err.println("JoglPipeline.lookupGLSLShaderAttrNames()"); + + // set the loc, type, and size arrays to out-of-bound values + for (int i = 0; i < attrNames.length; i++) { + locArr[i] = null; + typeArr[i] = -1; + sizeArr[i] = -1; + } + + // Loop through the list of active uniform variables, one at a + // time, searching for a match in the attrNames array. + // + // NOTE: Since attrNames isn't sorted, and we don't have a + // hashtable of names to index locations, we will do a + // brute-force, linear search of the array. This leads to an + // O(n^2) algorithm (actually O(n*m) where n is attrNames.length + // and m is the number of uniform variables), but since we expect + // N to be small, we will not optimize this at this time. + int id = unbox(shaderProgramId); + int[] tmp = new int[1]; + int[] tmp2 = new int[1]; + int[] tmp3 = new int[1]; + GL2 gl = context(ctx).getGL().getGL2(); + gl.glGetObjectParameterivARB(id, + GL2.GL_OBJECT_ACTIVE_UNIFORMS_ARB, + tmp, 0); + int numActiveUniforms = tmp[0]; + gl.glGetObjectParameterivARB(id, + GL2.GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, + tmp, 0); + int maxStrLen = tmp[0]; + byte[] nameBuf = new byte[maxStrLen]; + + for (int i = 0; i < numActiveUniforms; i++) { + gl.glGetActiveUniformARB(id, i, maxStrLen, tmp3, 0, + tmp, 0, + tmp2, 0, + nameBuf, 0); + int size = tmp[0]; + int type = tmp2[0]; + String name = null; + try { + // TODO KCR : Shouldn't this use the default locale? + name = new String(nameBuf, 0, tmp3[0], "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + // Issue 247 - we need to workaround an ATI bug where they erroneously + // report individual elements of arrays rather than the array itself + if (name.length() >= 3 && name.endsWith("]")) { + if (name.endsWith("[0]")) { + name = name.substring(0, name.length() - 3); + } else { + // Ignore this name + continue; + } + } + + // Now try to find the name + for (int j = 0; j < numAttrNames; j++) { + if (name.equals(attrNames[j])) { + sizeArr[j] = size; + isArrayArr[j] = (size > 1); + typeArr[j] = glslToJ3dType(type); + break; + } + } + } + + // Now lookup the location of each name in the attrNames array + for (int i = 0; i < numAttrNames; i++) { + // Get uniform attribute location + int loc = gl.glGetUniformLocationARB(id, attrNames[i]); + locArr[i] = new JoglShaderObject(loc); + } + } + + @Override + ShaderError useGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId) { + if (VERBOSE) System.err.println("JoglPipeline.useGLSLShaderProgram()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glUseProgramObjectARB(unbox(shaderProgramId)); + ((JoglContext) ctx).setShaderProgram((JoglShaderObject) shaderProgramId); + return null; + } + + //---------------------------------------------------------------------- + // Helper methods for above shader routines + // + private int unbox(ShaderAttrLoc loc) { + if (loc == null) + return 0; + return ((JoglShaderObject) loc).getValue(); + } + + private int unbox(ShaderProgramId id) { + if (id == null) + return 0; + return ((JoglShaderObject) id).getValue(); + } + + private int unbox(ShaderId id) { + if (id == null) + return 0; + return ((JoglShaderObject) id).getValue(); + } + + private String getInfoLog(GL2 gl, int id) { + int[] infoLogLength = new int[1]; + gl.glGetObjectParameterivARB(id, GL2.GL_OBJECT_INFO_LOG_LENGTH_ARB, infoLogLength, 0); + if (infoLogLength[0] > 0) { + byte[] storage = new byte[infoLogLength[0]]; + int[] len = new int[1]; + gl.glGetInfoLogARB(id, infoLogLength[0], len, 0, storage, 0); + try { + // TODO KCR : Shouldn't this use the default locale? + return new String(storage, 0, len[0], "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + return null; + } + + private int glslToJ3dType(int type) { + switch (type) { + case GL2.GL_BOOL_ARB: + case GL2.GL_INT: + case GL2.GL_SAMPLER_2D_ARB: + case GL2.GL_SAMPLER_3D_ARB: + case GL2.GL_SAMPLER_CUBE_ARB: + return ShaderAttributeObjectRetained.TYPE_INTEGER; + + case GL.GL_FLOAT: + return ShaderAttributeObjectRetained.TYPE_FLOAT; + + case GL2.GL_INT_VEC2_ARB: + case GL2.GL_BOOL_VEC2_ARB: + return ShaderAttributeObjectRetained.TYPE_TUPLE2I; + + case GL2.GL_FLOAT_VEC2_ARB: + return ShaderAttributeObjectRetained.TYPE_TUPLE2F; + + case GL2.GL_INT_VEC3_ARB: + case GL2.GL_BOOL_VEC3_ARB: + return ShaderAttributeObjectRetained.TYPE_TUPLE3I; + + case GL2.GL_FLOAT_VEC3_ARB: + return ShaderAttributeObjectRetained.TYPE_TUPLE3F; + + case GL2.GL_INT_VEC4_ARB: + case GL2.GL_BOOL_VEC4_ARB: + return ShaderAttributeObjectRetained.TYPE_TUPLE4I; + + case GL2.GL_FLOAT_VEC4_ARB: + return ShaderAttributeObjectRetained.TYPE_TUPLE4F; + + // case GL.GL_FLOAT_MAT2_ARB: + + case GL2.GL_FLOAT_MAT3_ARB: + return ShaderAttributeObjectRetained.TYPE_MATRIX3F; + + case GL2.GL_FLOAT_MAT4_ARB: + return ShaderAttributeObjectRetained.TYPE_MATRIX4F; + + // Java 3D does not support the following sampler types: + // + // case GL.GL_SAMPLER_1D_ARB: + // case GL.GL_SAMPLER_1D_SHADOW_ARB: + // case GL.GL_SAMPLER_2D_SHADOW_ARB: + // case GL.GL_SAMPLER_2D_RECT_ARB: + // case GL.GL_SAMPLER_2D_RECT_SHADOW_ARB: + } + + return -1; + } + + // --------------------------------------------------------------------- + + // + // ColoringAttributesRetained methods + // + + @Override + void updateColoringAttributes(Context ctx, + float dRed, float dGreen, float dBlue, + float red, float green, float blue, + float alpha, + boolean lightEnable, + int shadeModel) { + if (VERBOSE) System.err.println("JoglPipeline.updateColoringAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + float cr, cg, cb; + + if (lightEnable) { + cr = dRed; cg = dGreen; cb = dBlue; + } else { + cr = red; cg = green; cb = blue; + } + gl.glColor4f(cr, cg, cb, alpha); + if (shadeModel == ColoringAttributes.SHADE_FLAT) { + gl.glShadeModel(GL2.GL_FLAT); + } else { + gl.glShadeModel(GL2.GL_SMOOTH); + } + } + + + // --------------------------------------------------------------------- + + // + // DirectionalLightRetained methods + // + + private static final float[] black = new float[4]; + @Override + void updateDirectionalLight(Context ctx, + int lightSlot, float red, float green, + float blue, float dirx, float diry, float dirz) { + if (VERBOSE) System.err.println("JoglPipeline.updateDirectionalLight()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int lightNum = GL2.GL_LIGHT0 + lightSlot; + float[] values = new float[4]; + + values[0] = red; + values[1] = green; + values[2] = blue; + values[3] = 1.0f; + gl.glLightfv(lightNum, GL2.GL_DIFFUSE, values, 0); + gl.glLightfv(lightNum, GL2.GL_SPECULAR, values, 0); + values[0] = -dirx; + values[1] = -diry; + values[2] = -dirz; + values[3] = 0.0f; + gl.glLightfv(lightNum, GL2.GL_POSITION, values, 0); + gl.glLightfv(lightNum, GL2.GL_AMBIENT, black, 0); + gl.glLightf(lightNum, GL2.GL_CONSTANT_ATTENUATION, 1.0f); + gl.glLightf(lightNum, GL2.GL_LINEAR_ATTENUATION, 0.0f); + gl.glLightf(lightNum, GL2.GL_QUADRATIC_ATTENUATION, 0.0f); + gl.glLightf(lightNum, GL2.GL_SPOT_EXPONENT, 0.0f); + gl.glLightf(lightNum, GL2.GL_SPOT_CUTOFF, 180.0f); + } + + + // --------------------------------------------------------------------- + + // + // PointLightRetained methods + // + + @Override + void updatePointLight(Context ctx, + int lightSlot, float red, float green, + float blue, float attenx, float atteny, float attenz, + float posx, float posy, float posz) { + if (VERBOSE) System.err.println("JoglPipeline.updatePointLight()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int lightNum = GL2.GL_LIGHT0 + lightSlot; + float[] values = new float[4]; + + values[0] = red; + values[1] = green; + values[2] = blue; + values[3] = 1.0f; + gl.glLightfv(lightNum, GL2.GL_DIFFUSE, values, 0); + gl.glLightfv(lightNum, GL2.GL_SPECULAR, values, 0); + gl.glLightfv(lightNum, GL2.GL_AMBIENT, black, 0); + values[0] = posx; + values[1] = posy; + values[2] = posz; + gl.glLightfv(lightNum, GL2.GL_POSITION, values, 0); + gl.glLightf(lightNum, GL2.GL_CONSTANT_ATTENUATION, attenx); + gl.glLightf(lightNum, GL2.GL_LINEAR_ATTENUATION, atteny); + gl.glLightf(lightNum, GL2.GL_QUADRATIC_ATTENUATION, attenz); + gl.glLightf(lightNum, GL2.GL_SPOT_EXPONENT, 0.0f); + gl.glLightf(lightNum, GL2.GL_SPOT_CUTOFF, 180.0f); + } + + + // --------------------------------------------------------------------- + + // + // SpotLightRetained methods + // + + @Override + void updateSpotLight(Context ctx, + int lightSlot, float red, float green, + float blue, float attenx, float atteny, float attenz, + float posx, float posy, float posz, float spreadAngle, + float concentration, float dirx, float diry, + float dirz) { + if (VERBOSE) System.err.println("JoglPipeline.updateSpotLight()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int lightNum = GL2.GL_LIGHT0 + lightSlot; + float[] values = new float[4]; + + values[0] = red; + values[1] = green; + values[2] = blue; + values[3] = 1.0f; + gl.glLightfv(lightNum, GL2.GL_DIFFUSE, values, 0); + gl.glLightfv(lightNum, GL2.GL_SPECULAR, values, 0); + gl.glLightfv(lightNum, GL2.GL_AMBIENT, black, 0); + values[0] = posx; + values[1] = posy; + values[2] = posz; + gl.glLightfv(lightNum, GL2.GL_POSITION, values, 0); + gl.glLightf(lightNum, GL2.GL_CONSTANT_ATTENUATION, attenx); + gl.glLightf(lightNum, GL2.GL_LINEAR_ATTENUATION, atteny); + gl.glLightf(lightNum, GL2.GL_QUADRATIC_ATTENUATION, attenz); + values[0] = dirx; + values[1] = diry; + values[2] = dirz; + gl.glLightfv(lightNum, GL2.GL_SPOT_DIRECTION, values, 0); + gl.glLightf(lightNum, GL2.GL_SPOT_EXPONENT, concentration); + gl.glLightf(lightNum, GL2.GL_SPOT_CUTOFF, (float) (spreadAngle * 180.0f / Math.PI)); + } + + + // --------------------------------------------------------------------- + + // + // ExponentialFogRetained methods + // + + @Override + void updateExponentialFog(Context ctx, + float red, float green, float blue, + float density) { + if (VERBOSE) System.err.println("JoglPipeline.updateExponentialFog()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + float[] color = new float[3]; + color[0] = red; + color[1] = green; + color[2] = blue; + gl.glFogi(GL2.GL_FOG_MODE, GL2.GL_EXP); + gl.glFogfv(GL2.GL_FOG_COLOR, color, 0); + gl.glFogf(GL2.GL_FOG_DENSITY, density); + gl.glEnable(GL2.GL_FOG); + } + + + // --------------------------------------------------------------------- + + // + // LinearFogRetained methods + // + + @Override + void updateLinearFog(Context ctx, + float red, float green, float blue, + double fdist, double bdist) { + if (VERBOSE) System.err.println("JoglPipeline.updateLinearFog()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + float[] color = new float[3]; + color[0] = red; + color[1] = green; + color[2] = blue; + gl.glFogi(GL2.GL_FOG_MODE, GL.GL_LINEAR); + gl.glFogfv(GL2.GL_FOG_COLOR, color, 0); + gl.glFogf(GL2.GL_FOG_START, (float) fdist); + gl.glFogf(GL2.GL_FOG_END, (float) bdist); + gl.glEnable(GL2.GL_FOG); + } + + + // --------------------------------------------------------------------- + + // + // LineAttributesRetained methods + // + + @Override + void updateLineAttributes(Context ctx, + float lineWidth, int linePattern, + int linePatternMask, + int linePatternScaleFactor, + boolean lineAntialiasing) { + if (VERBOSE) System.err.println("JoglPipeline.updateLineAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glLineWidth(lineWidth); + + if (linePattern == LineAttributes.PATTERN_SOLID) { + gl.glDisable(GL2.GL_LINE_STIPPLE); + } else { + if (linePattern == LineAttributes.PATTERN_DASH) { // dashed lines + gl.glLineStipple(1, (short) 0x00ff); + } else if (linePattern == LineAttributes.PATTERN_DOT) { // dotted lines + gl.glLineStipple(1, (short) 0x0101); + } else if (linePattern == LineAttributes.PATTERN_DASH_DOT) { // dash-dotted lines + gl.glLineStipple(1, (short) 0x087f); + } else if (linePattern == LineAttributes.PATTERN_USER_DEFINED) { // user-defined mask + gl.glLineStipple(linePatternScaleFactor, (short) linePatternMask); + } + gl.glEnable(GL2.GL_LINE_STIPPLE); + } + + /* XXXX: Polygon Mode check, blend enable */ + if (lineAntialiasing) { + gl.glEnable(GL.GL_LINE_SMOOTH); + } else { + gl.glDisable(GL.GL_LINE_SMOOTH); + } + } + + + // --------------------------------------------------------------------- + + // + // MaterialRetained methods + // + + @Override + void updateMaterial(Context 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 lightEnable) { + if (VERBOSE) System.err.println("JoglPipeline.updateMaterial()"); + + float[] color = new float[4]; + + GL2 gl = context(ctx).getGL().getGL2(); + + gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL2.GL_SHININESS, shininess); + switch (colorTarget) { + case Material.DIFFUSE: + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE); + break; + case Material.AMBIENT: + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT); + break; + case Material.EMISSIVE: + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_EMISSION); + break; + case Material.SPECULAR: + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_SPECULAR); + break; + case Material.AMBIENT_AND_DIFFUSE: + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE); + break; + } + + color[0] = eRed; color[1] = eGreen; color[2] = eBlue; + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_EMISSION, color, 0); + + color[0] = aRed; color[1] = aGreen; color[2] = aBlue; + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT, color, 0); + + color[0] = sRed; color[1] = sGreen; color[2] = sBlue; + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_SPECULAR, color, 0); + + if (lightEnable) { + color[0] = dRed; color[1] = dGreen; color[2] = dBlue; + } else { + color[0] = red; color[1] = green; color[2] = blue; + } + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, color, 0); + gl.glColor4f(color[0], color[1], color[2], color[3]); + + if (lightEnable) { + gl.glEnable(GL2.GL_LIGHTING); + } else { + gl.glDisable(GL2.GL_LIGHTING); + } + } + + + // --------------------------------------------------------------------- + + // + // ModelClipRetained methods + // + + @Override + void updateModelClip(Context ctx, int planeNum, boolean enableFlag, + double A, double B, double C, double D) { + if (VERBOSE) System.err.println("JoglPipeline.updateModelClip()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + double[] equation = new double[4]; + int pl = GL2.GL_CLIP_PLANE0 + planeNum; + + // OpenGL clip planes are opposite to J3d clip planes + if (enableFlag) { + equation[0] = -A; + equation[1] = -B; + equation[2] = -C; + equation[3] = -D; + gl.glClipPlane(pl, DoubleBuffer.wrap(equation)); + gl.glEnable(pl); + } else { + gl.glDisable(pl); + } + } + + + // --------------------------------------------------------------------- + + // + // PointAttributesRetained methods + // + + @Override + void updatePointAttributes(Context ctx, float pointSize, boolean pointAntialiasing) { + if (VERBOSE) System.err.println("JoglPipeline.updatePointAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glPointSize(pointSize); + + // XXXX: Polygon Mode check, blend enable + if (pointAntialiasing) { + gl.glEnable(GL2.GL_POINT_SMOOTH); + } else { + gl.glDisable(GL2.GL_POINT_SMOOTH); + } + } + + + // --------------------------------------------------------------------- + + // + // PolygonAttributesRetained methods + // + + @Override + void updatePolygonAttributes(Context ctx, + int polygonMode, int cullFace, + boolean backFaceNormalFlip, + float polygonOffset, + float polygonOffsetFactor) { + if (VERBOSE) System.err.println("JoglPipeline.updatePolygonAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (cullFace == PolygonAttributes.CULL_NONE) { + gl.glDisable(GL.GL_CULL_FACE); + } else { + if (cullFace == PolygonAttributes.CULL_BACK) { + gl.glCullFace(GL.GL_BACK); + } else { + gl.glCullFace(GL.GL_FRONT); + } + gl.glEnable(GL.GL_CULL_FACE); + } + + if (backFaceNormalFlip && (cullFace != PolygonAttributes.CULL_BACK)) { + gl.glLightModeli(GL2.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE); + } else { + gl.glLightModeli(GL2.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_FALSE); + } + + if (polygonMode == PolygonAttributes.POLYGON_POINT) { + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_POINT); + } else if (polygonMode == PolygonAttributes.POLYGON_LINE) { + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE); + } else { + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL); + } + + gl.glPolygonOffset(polygonOffsetFactor, polygonOffset); + + if ((polygonOffsetFactor != 0.0) || (polygonOffset != 0.0)) { + switch (polygonMode) { + case PolygonAttributes.POLYGON_POINT: + gl.glEnable(GL2.GL_POLYGON_OFFSET_POINT); + gl.glDisable(GL2.GL_POLYGON_OFFSET_LINE); + gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); + break; + case PolygonAttributes.POLYGON_LINE: + gl.glEnable(GL2.GL_POLYGON_OFFSET_LINE); + gl.glDisable(GL2.GL_POLYGON_OFFSET_POINT); + gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); + break; + case PolygonAttributes.POLYGON_FILL: + gl.glEnable(GL.GL_POLYGON_OFFSET_FILL); + gl.glDisable(GL2.GL_POLYGON_OFFSET_POINT); + gl.glDisable(GL2.GL_POLYGON_OFFSET_LINE); + break; + } + } else { + gl.glDisable(GL2.GL_POLYGON_OFFSET_POINT); + gl.glDisable(GL2.GL_POLYGON_OFFSET_LINE); + gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); + } + } + + + // --------------------------------------------------------------------- + + // + // RenderingAttributesRetained methods + // + + @Override + void updateRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride, + boolean depthBufferEnable, + boolean depthBufferWriteEnable, + int depthTestFunction, + float alphaTestValue, int alphaTestFunction, + boolean ignoreVertexColors, + boolean rasterOpEnable, int rasterOp, + boolean userStencilAvailable, boolean stencilEnable, + int stencilFailOp, int stencilZFailOp, int stencilZPassOp, + int stencilFunction, int stencilReferenceValue, + int stencilCompareMask, int stencilWriteMask ) { + if (VERBOSE) System.err.println("JoglPipeline.updateRenderingAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (!depthBufferEnableOverride) { + if (depthBufferEnable) { + gl.glEnable(GL.GL_DEPTH_TEST); + gl.glDepthFunc(getFunctionValue(depthTestFunction)); + } else { + gl.glDisable(GL.GL_DEPTH_TEST); + } + } + + if (!depthBufferWriteEnableOverride) { + if (depthBufferWriteEnable) { + gl.glDepthMask(true); + } else { + gl.glDepthMask(false); + } + } + + if (alphaTestFunction == RenderingAttributes.ALWAYS) { + gl.glDisable(GL2.GL_ALPHA_TEST); + } else { + gl.glEnable(GL2.GL_ALPHA_TEST); + gl.glAlphaFunc(getFunctionValue(alphaTestFunction), alphaTestValue); + } + + if (ignoreVertexColors) { + gl.glDisable(GL2.GL_COLOR_MATERIAL); + } else { + gl.glEnable(GL2.GL_COLOR_MATERIAL); + } + + if (rasterOpEnable) { + gl.glEnable(GL.GL_COLOR_LOGIC_OP); + switch (rasterOp) { + case RenderingAttributes.ROP_CLEAR: + gl.glLogicOp(GL.GL_CLEAR); + break; + case RenderingAttributes.ROP_AND: + gl.glLogicOp(GL.GL_AND); + break; + case RenderingAttributes.ROP_AND_REVERSE: + gl.glLogicOp(GL.GL_AND_REVERSE); + break; + case RenderingAttributes.ROP_COPY: + gl.glLogicOp(GL.GL_COPY); + break; + case RenderingAttributes.ROP_AND_INVERTED: + gl.glLogicOp(GL.GL_AND_INVERTED); + break; + case RenderingAttributes.ROP_NOOP: + gl.glLogicOp(GL.GL_NOOP); + break; + case RenderingAttributes.ROP_XOR: + gl.glLogicOp(GL.GL_XOR); + break; + case RenderingAttributes.ROP_OR: + gl.glLogicOp(GL.GL_OR); + break; + case RenderingAttributes.ROP_NOR: + gl.glLogicOp(GL.GL_NOR); + break; + case RenderingAttributes.ROP_EQUIV: + gl.glLogicOp(GL.GL_EQUIV); + break; + case RenderingAttributes.ROP_INVERT: + gl.glLogicOp(GL.GL_INVERT); + break; + case RenderingAttributes.ROP_OR_REVERSE: + gl.glLogicOp(GL.GL_OR_REVERSE); + break; + case RenderingAttributes.ROP_COPY_INVERTED: + gl.glLogicOp(GL.GL_COPY_INVERTED); + break; + case RenderingAttributes.ROP_OR_INVERTED: + gl.glLogicOp(GL.GL_OR_INVERTED); + break; + case RenderingAttributes.ROP_NAND: + gl.glLogicOp(GL.GL_NAND); + break; + case RenderingAttributes.ROP_SET: + gl.glLogicOp(GL.GL_SET); + break; + } + } else { + gl.glDisable(GL.GL_COLOR_LOGIC_OP); + } + + if (userStencilAvailable) { + if (stencilEnable) { + gl.glEnable(GL.GL_STENCIL_TEST); + + gl.glStencilOp(getStencilOpValue(stencilFailOp), + getStencilOpValue(stencilZFailOp), + getStencilOpValue(stencilZPassOp)); + + gl.glStencilFunc(getFunctionValue(stencilFunction), + stencilReferenceValue, stencilCompareMask); + + gl.glStencilMask(stencilWriteMask); + + } else { + gl.glDisable(GL.GL_STENCIL_TEST); + } + } + } + + private int getFunctionValue(int func) { + switch (func) { + case RenderingAttributes.ALWAYS: + func = GL.GL_ALWAYS; + break; + case RenderingAttributes.NEVER: + func = GL.GL_NEVER; + break; + case RenderingAttributes.EQUAL: + func = GL.GL_EQUAL; + break; + case RenderingAttributes.NOT_EQUAL: + func = GL.GL_NOTEQUAL; + break; + case RenderingAttributes.LESS: + func = GL.GL_LESS; + break; + case RenderingAttributes.LESS_OR_EQUAL: + func = GL.GL_LEQUAL; + break; + case RenderingAttributes.GREATER: + func = GL.GL_GREATER; + break; + case RenderingAttributes.GREATER_OR_EQUAL: + func = GL.GL_GEQUAL; + break; + } + + return func; + } + + private int getStencilOpValue(int op) { + switch (op) { + case RenderingAttributes.STENCIL_KEEP: + op = GL.GL_KEEP; + break; + case RenderingAttributes.STENCIL_ZERO: + op = GL.GL_ZERO; + break; + case RenderingAttributes.STENCIL_REPLACE: + op = GL.GL_REPLACE; + break; + case RenderingAttributes.STENCIL_INCR: + op = GL.GL_INCR; + break; + case RenderingAttributes.STENCIL_DECR: + op = GL.GL_DECR; + break; + case RenderingAttributes.STENCIL_INVERT: + op = GL.GL_INVERT; + break; + } + + return op; + } + + + // --------------------------------------------------------------------- + + // + // TexCoordGenerationRetained methods + // + + /** + * This method updates the native context: + * trans contains eyeTovworld transform in d3d + * trans contains vworldToEye transform in ogl + */ + @Override + void updateTexCoordGeneration(Context 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[] vworldToEc) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexCoordGeneration()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + float[] planeS = new float[4]; + float[] planeT = new float[4]; + float[] planeR = new float[4]; + float[] planeQ = new float[4]; + + if (enable) { + gl.glEnable(GL2.GL_TEXTURE_GEN_S); + gl.glEnable(GL2.GL_TEXTURE_GEN_T); + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + gl.glEnable(GL2.GL_TEXTURE_GEN_R); + gl.glDisable(GL2.GL_TEXTURE_GEN_Q); + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + gl.glEnable(GL2.GL_TEXTURE_GEN_R); + gl.glEnable(GL2.GL_TEXTURE_GEN_Q); + } else { + gl.glDisable(GL2.GL_TEXTURE_GEN_R); + gl.glDisable(GL2.GL_TEXTURE_GEN_Q); + } + + if (genMode != TexCoordGeneration.SPHERE_MAP) { + planeS[0] = planeSx; planeS[1] = planeSy; + planeS[2] = planeSz; planeS[3] = planeSw; + planeT[0] = planeTx; planeT[1] = planeTy; + planeT[2] = planeTz; planeT[3] = planeTw; + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + planeR[0] = planeRx; planeR[1] = planeRy; + planeR[2] = planeRz; planeR[3] = planeRw; + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + planeR[0] = planeRx; planeR[1] = planeRy; + planeR[2] = planeRz; planeR[3] = planeRw; + planeQ[0] = planeQx; planeQ[1] = planeQy; + planeQ[2] = planeQz; planeQ[3] = planeQw; + } + } + + switch (genMode) { + case TexCoordGeneration.OBJECT_LINEAR: + gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_OBJECT_LINEAR); + gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_OBJECT_LINEAR); + gl.glTexGenfv(GL2.GL_S, GL2.GL_OBJECT_PLANE, planeS, 0); + gl.glTexGenfv(GL2.GL_T, GL2.GL_OBJECT_PLANE, planeT, 0); + + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_OBJECT_LINEAR); + gl.glTexGenfv(GL2.GL_R, GL2.GL_OBJECT_PLANE, planeR, 0); + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_OBJECT_LINEAR); + gl.glTexGenfv(GL2.GL_R, GL2.GL_OBJECT_PLANE, planeR, 0); + gl.glTexGeni(GL2.GL_Q, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_OBJECT_LINEAR); + gl.glTexGenfv(GL2.GL_Q, GL2.GL_OBJECT_PLANE, planeQ, 0); + } + break; + case TexCoordGeneration.EYE_LINEAR: + + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glPushMatrix(); + + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glLoadTransposeMatrixd(vworldToEc, 0); + } else { + double[] v = new double[16]; + copyTranspose(vworldToEc, v); + gl.glLoadMatrixd(v, 0); + } + + gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_EYE_LINEAR); + gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_EYE_LINEAR); + gl.glTexGenfv(GL2.GL_S, GL2.GL_EYE_PLANE, planeS, 0); + gl.glTexGenfv(GL2.GL_T, GL2.GL_EYE_PLANE, planeT, 0); + + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_EYE_LINEAR); + gl.glTexGenfv(GL2.GL_R, GL2.GL_EYE_PLANE, planeR, 0); + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_EYE_LINEAR); + gl.glTexGenfv(GL2.GL_R, GL2.GL_EYE_PLANE, planeR, 0); + gl.glTexGeni(GL2.GL_Q, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_EYE_LINEAR); + gl.glTexGenfv(GL2.GL_Q, GL2.GL_EYE_PLANE, planeQ, 0); + } + gl.glPopMatrix(); + break; + case TexCoordGeneration.SPHERE_MAP: + gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP); + gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP); + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP); + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP); + gl.glTexGeni(GL2.GL_Q, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP); + } + + break; + case TexCoordGeneration.NORMAL_MAP: + gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_NORMAL_MAP); + gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_NORMAL_MAP); + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_NORMAL_MAP); + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_NORMAL_MAP); + gl.glTexGeni(GL2.GL_Q, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_NORMAL_MAP); + } + break; + case TexCoordGeneration.REFLECTION_MAP: + gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); + gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); + if (format == TexCoordGeneration.TEXTURE_COORDINATE_3) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); + } else if (format == TexCoordGeneration.TEXTURE_COORDINATE_4) { + gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); + gl.glTexGeni(GL2.GL_Q, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); + } + break; + } + } else { + gl.glDisable(GL2.GL_TEXTURE_GEN_S); + gl.glDisable(GL2.GL_TEXTURE_GEN_T); + gl.glDisable(GL2.GL_TEXTURE_GEN_R); + gl.glDisable(GL2.GL_TEXTURE_GEN_Q); + } + } + + + // --------------------------------------------------------------------- + + // + // TransparencyAttributesRetained methods + // + + private static final int screen_door[][] = { + /* 0 / 16 */ + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + /* 1 / 16 */ + { + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + 0x00000000, 0x22222222, 0x00000000, 0x00000000, + }, + /* 2 / 16 */ + { + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + 0x00000000, 0x22222222, 0x00000000, 0x88888888, + }, + /* 3 / 16 */ + { + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0x88888888, + }, + /* 4 / 16 */ + { + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x00000000, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + }, + /* 5 / 16 */ + { + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x00000000, 0xaaaaaaaa, + }, + /* 6 / 16 */ + { + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x11111111, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + }, + /* 7 / 16 */ + { + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x44444444, 0xaaaaaaaa, + }, + /* 8 / 16 */ + { + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + }, + /* 9 / 16 */ + { + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + }, + /* 10 / 16 */ + { + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0x77777777, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + }, + /* 11 / 16 */ + { + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xdddddddd, 0xaaaaaaaa, + }, + /* 12 / 16 */ + { + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xaaaaaaaa, 0xffffffff, 0xaaaaaaaa, + }, + /* 13 / 16 */ + { + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xaaaaaaaa, + }, + /* 14 / 16 */ + { + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xbbbbbbbb, 0xffffffff, 0xeeeeeeee, + }, + /* 15 / 16 */ + { + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + 0xffffffff, 0xffffffff, 0xffffffff, 0xeeeeeeee, + }, + /* 16 / 16 */ + { + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + }, + }; + private static final ByteBuffer[] screen_door_table = new ByteBuffer[screen_door.length]; + static { + int eachLen = screen_door[0].length * Buffers.SIZEOF_INT; + ByteBuffer buf = Buffers.newDirectByteBuffer(screen_door.length * eachLen); + IntBuffer intBuf = buf.asIntBuffer(); + for (int i = 0; i < screen_door.length; i++) { + intBuf.put(screen_door[i]); + } + buf.rewind(); + for (int i = 0; i < screen_door.length; i++) { + buf.position(i * eachLen); + buf.limit((i+1) * eachLen); + screen_door_table[i] = buf.slice(); + } + } + + private static final int[] blendFunctionTable = new int[TransparencyAttributes.MAX_BLEND_FUNC_TABLE_SIZE]; + static { + blendFunctionTable[TransparencyAttributes.BLEND_ZERO] = GL.GL_ZERO; + blendFunctionTable[TransparencyAttributes.BLEND_ONE] = GL.GL_ONE; + blendFunctionTable[TransparencyAttributes.BLEND_SRC_ALPHA] = GL.GL_SRC_ALPHA; + blendFunctionTable[TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA] = GL.GL_ONE_MINUS_SRC_ALPHA; + blendFunctionTable[TransparencyAttributes.BLEND_DST_COLOR] = GL.GL_DST_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_ONE_MINUS_DST_COLOR] = GL.GL_ONE_MINUS_DST_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_SRC_COLOR] = GL.GL_SRC_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_ONE_MINUS_SRC_COLOR] = GL.GL_ONE_MINUS_SRC_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_CONSTANT_COLOR] = GL2.GL_CONSTANT_COLOR; + } + + @Override + void updateTransparencyAttributes(Context ctx, + float alpha, int geometryType, + int polygonMode, + boolean lineAA, boolean pointAA, + int transparencyMode, + int srcBlendFunction, + int dstBlendFunction) { + if (VERBOSE) System.err.println("JoglPipeline.updateTransparencyAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (transparencyMode != TransparencyAttributes.SCREEN_DOOR) { + gl.glDisable(GL2.GL_POLYGON_STIPPLE); + } else { + gl.glEnable(GL2.GL_POLYGON_STIPPLE); + gl.glPolygonStipple(screen_door_table[(int)(alpha * 16)]); + } + + if ((transparencyMode < TransparencyAttributes.SCREEN_DOOR) || + ((((geometryType & RenderMolecule.LINE) != 0) || + (polygonMode == PolygonAttributes.POLYGON_LINE)) + && lineAA) || + ((((geometryType & RenderMolecule.POINT) != 0) || + (polygonMode == PolygonAttributes.POLYGON_POINT)) + && pointAA)) { + gl.glEnable(GL.GL_BLEND); + // valid range of blendFunction 0..3 is already verified in shared code. + gl.glBlendFunc(blendFunctionTable[srcBlendFunction], blendFunctionTable[dstBlendFunction]); + } else { + gl.glDisable(GL.GL_BLEND); + } + } + + + // --------------------------------------------------------------------- + + // + // TextureAttributesRetained methods + // + + @Override + void updateTextureAttributes(Context ctx, + double[] transform, boolean isIdentity, int textureMode, + int perspCorrectionMode, + float textureBlendColorRed, + float textureBlendColorGreen, + float textureBlendColorBlue, + float textureBlendColorAlpha, + int textureFormat) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, + (perspCorrectionMode == TextureAttributes.NICEST) ? GL.GL_NICEST : GL.GL_FASTEST); + + // set OGL texture matrix + gl.glPushAttrib(GL2.GL_TRANSFORM_BIT); + gl.glMatrixMode(GL.GL_TEXTURE); + + if (isIdentity) { + gl.glLoadIdentity(); + } else if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glLoadTransposeMatrixd(transform, 0); + } else { + double[] mx = new double[16]; + copyTranspose(transform, mx); + gl.glLoadMatrixd(mx, 0); + } + + gl.glPopAttrib(); + + // set texture color + float[] color = new float[4]; + color[0] = textureBlendColorRed; + color[1] = textureBlendColorGreen; + color[2] = textureBlendColorBlue; + color[3] = textureBlendColorAlpha; + gl.glTexEnvfv(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_COLOR, color, 0); + + // set texture environment mode + + switch (textureMode) { + case TextureAttributes.MODULATE: + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE); + break; + case TextureAttributes.DECAL: + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_DECAL); + break; + case TextureAttributes.BLEND: + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL.GL_BLEND); + break; + case TextureAttributes.REPLACE: + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); + break; + case TextureAttributes.COMBINE: + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_COMBINE); + break; + } +// FIXME: GL_SGI_texture_color_table +// if (gl.isExtensionAvailable("GL_SGI_texture_color_table")) { +// gl.glDisable(GL.GL_TEXTURE_COLOR_TABLE_SGI); +// } + } + + @Override + void updateRegisterCombiners(Context absCtx, + double[] transform, boolean isIdentity, int textureMode, + int perspCorrectionMode, + float textureBlendColorRed, + float textureBlendColorGreen, + float textureBlendColorBlue, + float textureBlendColorAlpha, + int textureFormat, + int combineRgbMode, int combineAlphaMode, + int[] combineRgbSrc, int[] combineAlphaSrc, + int[] combineRgbFcn, int[] combineAlphaFcn, + int combineRgbScale, int combineAlphaScale) { +// FIXME: GL_NV_register_combiners +// if (VERBOSE) System.err.println("JoglPipeline.updateRegisterCombiners()"); +// +// JoglContext ctx = (JoglContext) absCtx; +// GL2 gl = context(ctx).getGL().getGL2(); +// +// if (perspCorrectionMode == TextureAttributes.NICEST) { +// gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); +// } else { +// gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_FASTEST); +// } +// +// // set OGL texture matrix +// gl.glPushAttrib(GL2.GL_TRANSFORM_BIT); +// gl.glMatrixMode(GL.GL_TEXTURE); +// +// if (isIdentity) { +// gl.glLoadIdentity(); +// } else if (gl.isExtensionAvailable("GL_VERSION_1_3")) { +// gl.glLoadTransposeMatrixd(transform, 0); +// } else { +// double[] mx = new double[16]; +// copyTranspose(transform, mx); +// gl.glLoadMatrixd(mx, 0); +// } +// +// gl.glPopAttrib(); +// +// // set texture color +// float[] color = new float[4]; +// color[0] = textureBlendColorRed; +// color[1] = textureBlendColorGreen; +// color[2] = textureBlendColorBlue; +// color[3] = textureBlendColorAlpha; +// gl.glTexEnvfv(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_COLOR, color, 0); +// +// // set texture environment mode +// gl.glEnable(GL.GL_REGISTER_COMBINERS_NV); +// int textureUnit = ctx.getCurrentTextureUnit(); +// int combinerUnit = ctx.getCurrentCombinerUnit(); +// int fragment; +// if (combinerUnit == GL.GL_COMBINER0_NV) { +// fragment = GL.GL_PRIMARY_COLOR_NV; +// } else { +// fragment = GL.GL_SPARE0_NV; +// } +// +// switch (textureMode) { +// case TextureAttributes.MODULATE: +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_A_NV, fragment, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_B_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_A_NV, fragment, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_B_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// +// gl.glCombinerOutputNV(combinerUnit, GL.GL_RGB, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// gl.glCombinerOutputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// break; +// +// case TextureAttributes.DECAL: +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_A_NV, fragment, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_B_NV, textureUnit, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_ALPHA); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_C_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_D_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_A_NV, fragment, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_B_NV, GL.GL_ZERO, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_ALPHA); +// +// gl.glCombinerOutputNV(combinerUnit, GL.GL_RGB, +// GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// gl.glCombinerOutputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// break; +// +// case TextureAttributes.BLEND: +// gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR0_NV, color, 0); +// +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_A_NV, fragment, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_B_NV, textureUnit, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_C_NV, GL.GL_CONSTANT_COLOR0_NV, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_D_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_A_NV, fragment, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_B_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// +// gl.glCombinerOutputNV(combinerUnit, GL.GL_RGB, +// GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// gl.glCombinerOutputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// break; +// +// case TextureAttributes.REPLACE: +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_A_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_B_NV, GL.GL_ZERO, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_A_NV, textureUnit, +// GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_B_NV, GL.GL_ZERO, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_ALPHA); +// +// gl.glCombinerOutputNV(combinerUnit, GL.GL_RGB, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// gl.glCombinerOutputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, false, false); +// break; +// +// case TextureAttributes.COMBINE: +// if (combineRgbMode == TextureAttributes.COMBINE_DOT3) { +// int color1 = getCombinerArg(gl, combineRgbSrc[0], textureUnit, combinerUnit); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_A_NV, color1, +// GL.GL_EXPAND_NORMAL_NV, GL.GL_RGB); +// int color2 = getCombinerArg(gl, combineRgbSrc[1], textureUnit, combinerUnit); +// gl.glCombinerInputNV(combinerUnit, GL.GL_RGB, +// GL.GL_VARIABLE_B_NV, color2, +// GL.GL_EXPAND_NORMAL_NV, GL.GL_RGB); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_A_NV, GL.GL_ZERO, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_ALPHA); +// gl.glCombinerInputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_VARIABLE_B_NV, GL.GL_ZERO, +// GL.GL_UNSIGNED_INVERT_NV, GL.GL_ALPHA); +// +// gl.glCombinerOutputNV(combinerUnit, GL.GL_RGB, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE/*SCALE_BY_FOUR_NV*/, GL.GL_NONE, true, +// false, false); +// gl.glCombinerOutputNV(combinerUnit, GL.GL_ALPHA, +// GL.GL_SPARE0_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, +// GL.GL_NONE, GL.GL_NONE, false, +// false, false); +// } +// break; +// } +// +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_A_NV, +// GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_B_NV, +// GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB); +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_C_NV, +// GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_D_NV, +// GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_E_NV, +// GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_F_NV, +// GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB); +// gl.glFinalCombinerInputNV(GL.GL_VARIABLE_G_NV, +// GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ALPHA); +// +// if (gl.isExtensionAvailable("GL_SGI_texture_color_table")) +// gl.glDisable(GL.GL_TEXTURE_COLOR_TABLE_SGI); + // GL_SGI_texture_color_table + } + + @Override + void updateTextureColorTable(Context ctx, int numComponents, + int colorTableSize, + int[] textureColorTable) { +// FIXME: GL_SGI_texture_color_table +// if (VERBOSE) System.err.println("JoglPipeline.updateTextureColorTable()"); +// +// GL gl = context(ctx).getGL(); +// if (gl.isExtensionAvailable("GL_SGI_texture_color_table")) { +// if (numComponents == 3) { +// gl.glColorTable(GL.GL_TEXTURE_COLOR_TABLE_SGI, GL.GL_RGB, +// colorTableSize, GL.GL_RGB, GL2.GL_INT, IntBuffer.wrap(textureColorTable)); +// } else { +// gl.glColorTable(GL.GL_TEXTURE_COLOR_TABLE_SGI, GL.GL_RGBA, +// colorTableSize, GL.GL_RGBA, GL2.GL_INT, IntBuffer.wrap(textureColorTable)); +// } +// gl.glEnable(GL.GL_TEXTURE_COLOR_TABLE_SGI); +// } + } + + @Override + void updateCombiner(Context ctx, + int combineRgbMode, int combineAlphaMode, + int[] combineRgbSrc, int[] combineAlphaSrc, + int[] combineRgbFcn, int[] combineAlphaFcn, + int combineRgbScale, int combineAlphaScale) { + if (VERBOSE) System.err.println("JoglPipeline.updateCombiner()"); + + GL2 gl = context(ctx).getGL().getGL2(); + int[] GLrgbMode = new int[1]; + int[] GLalphaMode = new int[1]; + getGLCombineMode(gl, combineRgbMode, combineAlphaMode, + GLrgbMode, GLalphaMode); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, GLrgbMode[0]); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_ALPHA, GLalphaMode[0]); + + int nargs; + if (combineRgbMode == TextureAttributes.COMBINE_REPLACE) { + nargs = 1; + } else if (combineRgbMode == TextureAttributes.COMBINE_INTERPOLATE) { + nargs = 3; + } else { + nargs = 2; + } + + for (int i = 0; i < nargs; i++) { + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, _gl_combineRgbSrcIndex[i], + _gl_combineSrc[combineRgbSrc[i]]); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, _gl_combineRgbOpIndex[i], + _gl_combineFcn[combineRgbFcn[i]]); + } + + if (combineAlphaMode == TextureAttributes.COMBINE_REPLACE) { + nargs = 1; + } else if (combineAlphaMode == TextureAttributes.COMBINE_INTERPOLATE) { + nargs = 3; + } else { + nargs = 2; + } + + for (int i = 0; i < nargs; i++) { + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, _gl_combineAlphaSrcIndex[i], + _gl_combineSrc[combineAlphaSrc[i]]); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, _gl_combineAlphaOpIndex[i], + _gl_combineFcn[combineAlphaFcn[i]]); + } + + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_RGB_SCALE, combineRgbScale); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_ALPHA_SCALE, combineAlphaScale); + } + + // Helper routines for above + + private void getGLCombineMode(GL gl, int combineRgbMode, int combineAlphaMode, + int[] GLrgbMode, int[] GLalphaMode) { + switch (combineRgbMode) { + case TextureAttributes.COMBINE_REPLACE: + GLrgbMode[0] = GL.GL_REPLACE; + break; + case TextureAttributes.COMBINE_MODULATE: + GLrgbMode[0] = GL2.GL_MODULATE; + break; + case TextureAttributes.COMBINE_ADD: + GLrgbMode[0] = GL2.GL_ADD; + break; + case TextureAttributes.COMBINE_ADD_SIGNED: + GLrgbMode[0] = GL2.GL_ADD_SIGNED; + break; + case TextureAttributes.COMBINE_SUBTRACT: + GLrgbMode[0] = GL2.GL_SUBTRACT; + break; + case TextureAttributes.COMBINE_INTERPOLATE: + GLrgbMode[0] = GL2.GL_INTERPOLATE; + break; + case TextureAttributes.COMBINE_DOT3: + GLrgbMode[0] = GL2.GL_DOT3_RGB; + break; + default: + break; + } + + switch (combineAlphaMode) { + case TextureAttributes.COMBINE_REPLACE: + GLalphaMode[0] = GL.GL_REPLACE; + break; + case TextureAttributes.COMBINE_MODULATE: + GLalphaMode[0] = GL2.GL_MODULATE; + break; + case TextureAttributes.COMBINE_ADD: + GLalphaMode[0] = GL2.GL_ADD; + break; + case TextureAttributes.COMBINE_ADD_SIGNED: + GLalphaMode[0] = GL2.GL_ADD_SIGNED; + break; + case TextureAttributes.COMBINE_SUBTRACT: + GLalphaMode[0] = GL2.GL_SUBTRACT; + break; + case TextureAttributes.COMBINE_INTERPOLATE: + GLalphaMode[0] = GL2.GL_INTERPOLATE; + break; + case TextureAttributes.COMBINE_DOT3: + // dot3 will only make sense for alpha if rgb is also + // doing dot3. So if rgb is not doing dot3, fallback to replace + if (combineRgbMode == TextureAttributes.COMBINE_DOT3) { + GLrgbMode[0] = GL2.GL_DOT3_RGBA; + } else { + GLalphaMode[0] = GL.GL_REPLACE; + } + break; + default: + break; + } + } + + // mapping from java enum to gl enum + private static final int[] _gl_combineRgbSrcIndex = { + GL2.GL_SOURCE0_RGB, + GL2.GL_SOURCE1_RGB, + GL2.GL_SOURCE2_RGB, + }; + + private static final int[] _gl_combineAlphaSrcIndex = { + GL2.GL_SOURCE0_ALPHA, + GL2.GL_SOURCE1_ALPHA, + GL2.GL_SOURCE2_ALPHA, + }; + + private static final int[] _gl_combineRgbOpIndex = { + GL2.GL_OPERAND0_RGB, + GL2.GL_OPERAND1_RGB, + GL2.GL_OPERAND2_RGB, + }; + + private static final int[] _gl_combineAlphaOpIndex = { + GL2.GL_OPERAND0_ALPHA, + GL2.GL_OPERAND1_ALPHA, + GL2.GL_OPERAND2_ALPHA, + }; + + private static final int[] _gl_combineSrc = { + GL2.GL_PRIMARY_COLOR, // TextureAttributes.COMBINE_OBJECT_COLOR + GL.GL_TEXTURE, // TextureAttributes.COMBINE_TEXTURE + GL2.GL_CONSTANT, // TextureAttributes.COMBINE_CONSTANT_COLOR + GL2.GL_PREVIOUS, // TextureAttributes.COMBINE_PREVIOUS_TEXTURE_UNIT_STATE + }; + + private static final int[] _gl_combineFcn = { + GL.GL_SRC_COLOR, // TextureAttributes.COMBINE_SRC_COLOR + GL.GL_ONE_MINUS_SRC_COLOR, // TextureAttributes.COMBINE_ONE_MINUS_SRC_COLOR + GL.GL_SRC_ALPHA, // TextureAttributes.COMBINE_SRC_ALPHA + GL.GL_ONE_MINUS_SRC_ALPHA, // TextureAttributes.COMBINE_ONE_MINUS_SRC_ALPHA + }; + +// FIXME: GL_NV_register_combiners +// private int getCombinerArg(GL gl, int arg, int textureUnit, int combUnit) { +// int comb = 0; +// +// switch (arg) { +// case TextureAttributes.COMBINE_OBJECT_COLOR: +// if (combUnit == GL.GL_COMBINER0_NV) { +// comb = GL.GL_PRIMARY_COLOR_NV; +// } else { +// comb = GL.GL_SPARE0_NV; +// } +// break; +// case TextureAttributes.COMBINE_TEXTURE_COLOR: +// comb = textureUnit; +// break; +// case TextureAttributes.COMBINE_CONSTANT_COLOR: +// comb = GL.GL_CONSTANT_COLOR0_NV; +// break; +// case TextureAttributes.COMBINE_PREVIOUS_TEXTURE_UNIT_STATE: +// comb = textureUnit -1; +// break; +// } +// +// return comb; +// } + + + // --------------------------------------------------------------------- + + // + // TextureUnitStateRetained methods + // + + @Override + void updateTextureUnitState(Context ctx, int index, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureUnitState()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (index >= 0 && gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glActiveTexture(index + GL.GL_TEXTURE0); + gl.glClientActiveTexture(GL.GL_TEXTURE0 + index); +// FIXME: GL_NV_register_combiners +// if (gl.isExtensionAvailable("GL_NV_register_combiners")) { +// jctx.setCurrentTextureUnit(index + GL.GL_TEXTURE0); +// jctx.setCurrentCombinerUnit(index + GL.GL_COMBINER0_NV); +// gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, index + 1); +// } + } + + if (!enable) { + // if not enabled, then don't enable any tex mapping + gl.glDisable(GL2.GL_TEXTURE_1D); + gl.glDisable(GL.GL_TEXTURE_2D); + gl.glDisable(GL2.GL_TEXTURE_3D); + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + } + + // if it is enabled, the enable flag will be taken care of + // in the bindTexture call + } + + + // --------------------------------------------------------------------- + + // + // TextureRetained methods + // Texture2DRetained methods + // + + @Override + void bindTexture2D(Context ctx, int objectId, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.bindTexture2D(objectId=" + objectId + ",enable=" + enable + ")"); + + GL gl = context(ctx).getGL(); + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + gl.glDisable(GL2.GL_TEXTURE_3D); + + if (!enable) { + gl.glDisable(GL.GL_TEXTURE_2D); + } else { + gl.glBindTexture(GL.GL_TEXTURE_2D, objectId); + gl.glEnable(GL.GL_TEXTURE_2D); + } + } + + @Override + void updateTexture2DImage(Context ctx, + int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int dataType, Object data, boolean useAutoMipMap) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DImage(width=" + width + ",height=" + height + ",level=" + level + ")"); + + updateTexture2DImage(ctx, GL.GL_TEXTURE_2D, + numLevels, level, textureFormat, imageFormat, + width, height, boundaryWidth, dataType, data, useAutoMipMap); + } + + @Override + void updateTexture2DSubImage(Context ctx, + int level, int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int dataType, Object data, boolean useAutoMipMap) { + + /* Note: useAutoMipMap is not use for SubImage in the jogl pipe */ + + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DSubImage()"); + + updateTexture2DSubImage(ctx, GL.GL_TEXTURE_2D, + level, xoffset, yoffset, + textureFormat, imageFormat, + imgXOffset, imgYOffset, tilew, width, height, + dataType, data); + } + + @Override + void updateTexture2DLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLOD, float maximumLOD) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DLodRange()"); + + updateTextureLodRange(ctx, GL.GL_TEXTURE_2D, + baseLevel, maximumLevel, + minimumLOD, maximumLOD); + } + + @Override + void updateTexture2DLodOffset(Context ctx, + float lodOffsetS, float lodOffsetT, + float lodOffsetR) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DLodOffset()"); + + updateTextureLodOffset(ctx, GL.GL_TEXTURE_2D, + lodOffsetS, lodOffsetT, lodOffsetR); + } + + @Override + void updateTexture2DBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DBoundary()"); + + updateTextureBoundary(ctx, GL.GL_TEXTURE_2D, + boundaryModeS, boundaryModeT, -1, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + @Override + void updateTexture2DFilterModes(Context ctx, + int minFilter, int magFilter) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DFilterModes()"); + + updateTextureFilterModes(ctx, GL.GL_TEXTURE_2D, minFilter, magFilter); + } + + @Override + void updateTexture2DSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DSharpenFunc()"); + + updateTextureSharpenFunc(ctx, GL.GL_TEXTURE_2D, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + @Override + void updateTexture2DFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DFilter4Func()"); + + updateTextureFilter4Func(ctx, GL.GL_TEXTURE_2D, + numFilter4FuncPts, filter4FuncPts); + } + + @Override + void updateTexture2DAnisotropicFilter(Context ctx, float degree) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture2DAnisotropicFilter()"); + + updateTextureAnisotropicFilter(ctx, GL.GL_TEXTURE_2D, degree); + } + + private void updateTextureLodRange(Context ctx, + int target, + int baseLevel, int maximumLevel, + float minimumLOD, float maximumLOD) { + GL gl = context(ctx).getGL(); + // checking of the availability of the extension is already done + // in the shared code + gl.glTexParameteri(target, GL2.GL_TEXTURE_BASE_LEVEL, baseLevel); + gl.glTexParameteri(target, GL2.GL_TEXTURE_MAX_LEVEL, maximumLevel); + gl.glTexParameterf(target, GL2.GL_TEXTURE_MIN_LOD, minimumLOD); + gl.glTexParameterf(target, GL2.GL_TEXTURE_MAX_LOD, maximumLOD); + } + + private void updateTextureLodOffset(Context ctx, + int target, + float lodOffsetS, float lodOffsetT, + float lodOffsetR) { +// FIXME: GL_SGIX_texture_lod_bias +// GL gl = context(ctx).getGL(); + // checking of the availability of the extension is already done + // in the shared code +// gl.glTexParameterf(target, GL.GL_TEXTURE_LOD_BIAS_S_SGIX, lodOffsetS); +// gl.glTexParameterf(target, GL.GL_TEXTURE_LOD_BIAS_T_SGIX, lodOffsetT); +// gl.glTexParameterf(target, GL.GL_TEXTURE_LOD_BIAS_R_SGIX, lodOffsetR); + } + + private void updateTextureAnisotropicFilter(Context ctx, int target, float degree) { + GL gl = context(ctx).getGL(); + // checking of the availability of anisotropic filter functionality + // is already done in the shared code + gl.glTexParameterf(target, + GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, + degree); + } + + // --------------------------------------------------------------------- + + // + // Texture3DRetained methods + // + + @Override + void bindTexture3D(Context ctx, int objectId, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.bindTexture3D()"); + + GL gl = context(ctx).getGL(); + // textureCubeMap will take precedure over 3D Texture + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + + if (!enable) { + gl.glDisable(GL2.GL_TEXTURE_3D); + } else { + gl.glBindTexture(GL2.GL_TEXTURE_3D, objectId); + gl.glEnable(GL2.GL_TEXTURE_3D); + } + } + + @Override + void updateTexture3DImage(Context ctx, + int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, int depth, + int boundaryWidth, + int dataType, Object data, boolean useAutoMipMap) { + + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DImage()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int format = 0; + int internalFormat = 0; + int type = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + + switch (textureFormat) { + case Texture.INTENSITY: + internalFormat = GL2.GL_INTENSITY; + break; + case Texture.LUMINANCE: + internalFormat = GL.GL_LUMINANCE; + break; + case Texture.ALPHA: + internalFormat = GL.GL_ALPHA; + break; + case Texture.LUMINANCE_ALPHA: + internalFormat = GL.GL_LUMINANCE_ALPHA; + break; + case Texture.RGB: + internalFormat = GL.GL_RGB; + break; + case Texture.RGBA: + internalFormat = GL.GL_RGBA; + break; + default: + assert false; + return; + } + + if (useAutoMipMap) { + gl.glTexParameteri(GL2.GL_TEXTURE_3D, GL2.GL_GENERATE_MIPMAP, GL.GL_TRUE); + } + else { + gl.glTexParameteri(GL2.GL_TEXTURE_3D, GL2.GL_GENERATE_MIPMAP, GL.GL_FALSE); + } + + if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) { + + switch (imageFormat) { + case ImageComponentRetained.TYPE_BYTE_BGR: + format = GL2.GL_BGR; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + format = GL.GL_RGB; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) { // If its zero, should never come here! + format = GL2.GL_ABGR_EXT; + } else { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + format = GL.GL_RGBA; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + format = GL.GL_LUMINANCE_ALPHA; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + if (internalFormat == GL.GL_ALPHA) { + format = GL.GL_ALPHA; + } else { + format = GL.GL_LUMINANCE; + } + break; + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) { + + gl.glTexImage3D(GL2.GL_TEXTURE_3D, + level, internalFormat, + width, height, depth, boundaryWidth, + format, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap((byte[]) data)); + } + else { + gl.glTexImage3D(GL2.GL_TEXTURE_3D, + level, internalFormat, + width, height, depth, boundaryWidth, + format, GL.GL_UNSIGNED_BYTE, (ByteBuffer) data); + } + + } else if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) { + + switch (imageFormat) { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + format = GL.GL_RGBA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + format = GL2.GL_BGRA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* This method only supports 3 and 4 components formats and INT types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + + /* Force Alpha to 1.0 if needed */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + } + + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) { + gl.glTexImage3D(GL2.GL_TEXTURE_3D, + level, internalFormat, + width, height, depth, boundaryWidth, + format, type, IntBuffer.wrap((int[]) data)); + } else { + gl.glTexImage3D(GL2.GL_TEXTURE_3D, + level, internalFormat, + width, height, depth, boundaryWidth, + format, type, (Buffer) data); + } + + /* Restore Alpha scale and bias */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + } + } else { + assert false; + } + } + + @Override + void updateTexture3DSubImage(Context ctx, + int level, + int xoffset, int yoffset, int zoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, int imgZOffset, + int tilew, int tileh, + int width, int height, int depth, + int dataType, Object data, boolean useAutoMipMap) { + + /* Note: useAutoMipMap is not use for SubImage in the jogl pipe */ + + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DSubImage()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int format = 0; + int internalFormat = 0; + int type = GL2.GL_UNSIGNED_INT_8_8_8_8; + int numBytes = 0; + boolean forceAlphaToOne = false; + boolean pixelStore = false; + + if (imgXOffset > 0 || (width < tilew)) { + pixelStore = true; + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, tilew); + } + + switch (textureFormat) { + case Texture.INTENSITY: + internalFormat = GL2.GL_INTENSITY; + break; + case Texture.LUMINANCE: + internalFormat = GL.GL_LUMINANCE; + break; + case Texture.ALPHA: + internalFormat = GL.GL_ALPHA; + break; + case Texture.LUMINANCE_ALPHA: + internalFormat = GL.GL_LUMINANCE_ALPHA; + break; + case Texture.RGB: + internalFormat = GL.GL_RGB; + break; + case Texture.RGBA: + internalFormat = GL.GL_RGBA; + break; + default: + assert false; + } + + if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) { + + switch (imageFormat) { + case ImageComponentRetained.TYPE_BYTE_BGR: + format = GL2.GL_BGR; + numBytes = 3; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + format = GL.GL_RGB; + numBytes = 3; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) { // If its zero, should never come here! + format = GL2.GL_ABGR_EXT; + numBytes = 4; + } else { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + format = GL.GL_RGBA; + numBytes = 4; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + format = GL.GL_LUMINANCE_ALPHA; + numBytes = 2; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + if (internalFormat == GL.GL_ALPHA) { + format = GL.GL_ALPHA; + numBytes = 1; + } else { + format = GL.GL_LUMINANCE; + numBytes = 1; + } + break; + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + ByteBuffer buf = null; + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) { + buf = ByteBuffer.wrap((byte[]) data); + } + else { + buf = (ByteBuffer) data; + } + + int offset = (tilew * tileh * imgZOffset + + tilew * imgYOffset + imgXOffset) * numBytes; + buf.position(offset); + gl.glTexSubImage3D(GL2.GL_TEXTURE_3D, + level, xoffset, yoffset, zoffset, + width, height, depth, + format, GL.GL_UNSIGNED_BYTE, + buf); + + } else if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) { + + switch (imageFormat) { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + format = GL.GL_RGBA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + format = GL2.GL_BGRA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* This method only supports 3 and 4 components formats and INT types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + + /* Force Alpha to 1.0 if needed */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + } + + IntBuffer buf = null; + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) { + buf = IntBuffer.wrap((int[]) data); + } + else { + buf = (IntBuffer) data; + } + + int offset = tilew * tileh * imgZOffset + + tilew * imgYOffset + imgXOffset; + buf.position(offset); + gl.glTexSubImage3D(GL2.GL_TEXTURE_3D, + level, xoffset, yoffset, zoffset, + width, height, depth, + format, type, + buf); + + /* Restore Alpha scale and bias */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + } + } else { + assert false; + return; + } + + if (pixelStore) { + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, 0); + } + + } + + + @Override + void updateTexture3DLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DLodRange()"); + + updateTextureLodRange(ctx, GL2.GL_TEXTURE_3D, + baseLevel, maximumLevel, + minimumLod, maximumLod); + } + + @Override + void updateTexture3DLodOffset(Context ctx, + float lodOffsetS, float lodOffsetT, + float lodOffsetR) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DLodOffset()"); + + updateTextureLodOffset(ctx, GL2.GL_TEXTURE_3D, + lodOffsetS, lodOffsetT, lodOffsetR); + } + + @Override + void updateTexture3DBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + int boundaryModeR, float boundaryRed, + float boundaryGreen, float boundaryBlue, + float boundaryAlpha) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DBoundary()"); + + updateTextureBoundary(ctx, GL2.GL_TEXTURE_3D, + boundaryModeS, boundaryModeT, boundaryModeR, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + @Override + void updateTexture3DFilterModes(Context ctx, + int minFilter, int magFilter) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DFilterModes()"); + + updateTextureFilterModes(ctx, GL2.GL_TEXTURE_3D, + minFilter, magFilter); + } + + @Override + void updateTexture3DSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DSharpenFunc()"); + + updateTextureSharpenFunc(ctx, GL2.GL_TEXTURE_3D, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + @Override + void updateTexture3DFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DFilter4Func()"); + + updateTextureFilter4Func(ctx, GL2.GL_TEXTURE_3D, + numFilter4FuncPts, filter4FuncPts); + } + + @Override + void updateTexture3DAnisotropicFilter(Context ctx, float degree) { + if (VERBOSE) System.err.println("JoglPipeline.updateTexture3DAnisotropicFilter()"); + + updateTextureAnisotropicFilter(ctx, GL2.GL_TEXTURE_3D, degree); + } + + + // --------------------------------------------------------------------- + + // + // TextureCubeMapRetained methods + // + + @Override + void bindTextureCubeMap(Context ctx, int objectId, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.bindTextureCubeMap()"); + + GL gl = context(ctx).getGL(); + // TextureCubeMap will take precedure over 3D Texture so + // there is no need to disable 3D Texture here. + if (!enable) { + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + } else { + gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, objectId); + gl.glEnable(GL.GL_TEXTURE_CUBE_MAP); + } + } + + @Override + void updateTextureCubeMapImage(Context ctx, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int dataType, Object data, boolean useAutoMipMap) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapImage()"); + + updateTexture2DImage(ctx, _gl_textureCubeMapFace[face], + numLevels, level, textureFormat, imageFormat, + width, height, boundaryWidth, dataType, data, useAutoMipMap); + } + + @Override + void updateTextureCubeMapSubImage(Context ctx, + int face, int level, int xoffset, int yoffset, + int textureFormat,int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int dataType, Object data, boolean useAutoMipMap) { + + /* Note: useAutoMipMap is not use for SubImage in the jogl pipe */ + + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapSubImage()"); + + updateTexture2DSubImage(ctx, _gl_textureCubeMapFace[face], + level, xoffset, yoffset, textureFormat, + imageFormat, imgXOffset, imgYOffset, tilew, + width, height, dataType, data); + } + + @Override + void updateTextureCubeMapLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapLodRange()"); + + updateTextureLodRange(ctx, + GL.GL_TEXTURE_CUBE_MAP, + baseLevel, maximumLevel, + minimumLod, maximumLod); + } + + @Override + void updateTextureCubeMapLodOffset(Context ctx, + float lodOffsetS, float lodOffsetT, + float lodOffsetR) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapLodOffset()"); + + updateTextureLodOffset(ctx, + GL.GL_TEXTURE_CUBE_MAP, + lodOffsetS, lodOffsetT, lodOffsetR); + } + + @Override + void updateTextureCubeMapBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapBoundary()"); + + updateTextureBoundary(ctx, + GL.GL_TEXTURE_CUBE_MAP, + boundaryModeS, boundaryModeT, -1, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + @Override + void updateTextureCubeMapFilterModes(Context ctx, + int minFilter, int magFilter) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapFilterModes()"); + + updateTextureFilterModes(ctx, + GL.GL_TEXTURE_CUBE_MAP, + minFilter, magFilter); + } + + @Override + void updateTextureCubeMapSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapSharpenFunc()"); + + updateTextureSharpenFunc(ctx, + GL.GL_TEXTURE_CUBE_MAP, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + @Override + void updateTextureCubeMapFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapFilter4Func()"); + + updateTextureFilter4Func(ctx, + GL.GL_TEXTURE_CUBE_MAP, + numFilter4FuncPts, filter4FuncPts); + } + + @Override + void updateTextureCubeMapAnisotropicFilter(Context ctx, float degree) { + if (VERBOSE) System.err.println("JoglPipeline.updateTextureCubeMapAnisotropicFilter()"); + + updateTextureAnisotropicFilter(ctx, + GL.GL_TEXTURE_CUBE_MAP, + degree); + } + + //---------------------------------------------------------------------- + // + // Helper routines for above texture methods + // + + private void updateTexture2DImage(Context ctx, + int target, + int numLevels, + int level, + int textureFormat, + int imageFormat, + int width, + int height, + int boundaryWidth, + int dataType, + Object data, + boolean useAutoMipMap) { + GL2 gl = context(ctx).getGL().getGL2(); + + int format = 0, internalFormat = 0; + int type = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + + switch (textureFormat) { + case Texture.INTENSITY: + internalFormat = GL2.GL_INTENSITY; + break; + case Texture.LUMINANCE: + internalFormat = GL.GL_LUMINANCE; + break; + case Texture.ALPHA: + internalFormat = GL.GL_ALPHA; + break; + case Texture.LUMINANCE_ALPHA: + internalFormat = GL.GL_LUMINANCE_ALPHA; + break; + case Texture.RGB: + internalFormat = GL.GL_RGB; + break; + case Texture.RGBA: + internalFormat = GL.GL_RGBA; + break; + default: + assert false; + } + + if (useAutoMipMap) { + gl.glTexParameteri(target, GL2.GL_GENERATE_MIPMAP, GL.GL_TRUE); + } + else { + gl.glTexParameteri(target, GL2.GL_GENERATE_MIPMAP, GL.GL_FALSE); + } + + if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) { + + switch (imageFormat) { + case ImageComponentRetained.TYPE_BYTE_BGR: + format = GL2.GL_BGR; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + format = GL.GL_RGB; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) { // If its zero, should never come here! + format = GL2.GL_ABGR_EXT; + } else { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + format = GL.GL_RGBA; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + format = GL.GL_LUMINANCE_ALPHA; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + if (internalFormat == GL.GL_ALPHA) { + format = GL.GL_ALPHA; + } else { + format = GL.GL_LUMINANCE; + } + break; + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) { + + gl.glTexImage2D(target, level, internalFormat, + width, height, boundaryWidth, + format, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap((byte[])data)); + } else { + gl.glTexImage2D(target, level, internalFormat, + width, height, boundaryWidth, + format, GL.GL_UNSIGNED_BYTE, (Buffer) data); + } + + } else if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) { + + switch (imageFormat) { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + format = GL.GL_RGBA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + format = GL2.GL_BGRA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* This method only supports 3 and 4 components formats and INT types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + + /* Force Alpha to 1.0 if needed */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + } + + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) { + gl.glTexImage2D(target, level, internalFormat, + width, height, boundaryWidth, + format, type, IntBuffer.wrap((int[])data)); + } else { + gl.glTexImage2D(target, level, internalFormat, + width, height, boundaryWidth, + format, type, (Buffer) data); + } + + /* Restore Alpha scale and bias */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + } + } else { + assert false; + } + } + + private void updateTexture2DSubImage(Context ctx, + int target, + int level, int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int dataType, Object data) { + GL2 gl = context(ctx).getGL().getGL2(); + + int format = 0, internalFormat=0; + int numBytes = 0; + int type = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + boolean pixelStore = false; + + if (imgXOffset > 0 || (width < tilew)) { + pixelStore = true; + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, tilew); + } + + switch (textureFormat) { + case Texture.INTENSITY: + internalFormat = GL2.GL_INTENSITY; + break; + case Texture.LUMINANCE: + internalFormat = GL.GL_LUMINANCE; + break; + case Texture.ALPHA: + internalFormat = GL.GL_ALPHA; + break; + case Texture.LUMINANCE_ALPHA: + internalFormat = GL.GL_LUMINANCE_ALPHA; + break; + case Texture.RGB: + internalFormat = GL.GL_RGB; + break; + case Texture.RGBA: + internalFormat = GL.GL_RGBA; + break; + default: + assert false; + } + + if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) { + + switch (imageFormat) { + case ImageComponentRetained.TYPE_BYTE_BGR: + format = GL2.GL_BGR; + numBytes = 3; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + format = GL.GL_RGB; + numBytes = 3; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) { // If its zero, should never come here! + format = GL2.GL_ABGR_EXT; + numBytes = 4; + } else { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + format = GL.GL_RGBA; + numBytes = 4; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + format = GL.GL_LUMINANCE_ALPHA; + numBytes = 2; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + if (internalFormat == GL.GL_ALPHA) { + format = GL.GL_ALPHA; + numBytes = 1; + } else { + format = GL.GL_LUMINANCE; + numBytes = 1; + } + break; + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + ByteBuffer buf = null; + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) { + buf = ByteBuffer.wrap((byte[]) data); + } + else { + buf = (ByteBuffer) data; + } + + // offset by the imageOffset + buf.position((tilew * imgYOffset + imgXOffset) * numBytes); + gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, + format, GL.GL_UNSIGNED_BYTE, buf); + + } else if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) { + + switch (imageFormat) { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + format = GL.GL_RGBA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + format = GL2.GL_BGRA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* This method only supports 3 and 4 components formats and INT types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + /* Force Alpha to 1.0 if needed */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + } + + IntBuffer buf = null; + if(dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) { + buf = IntBuffer.wrap((int[]) data); + } + else { + buf = (IntBuffer) data; + } + + // offset by the imageOffset + buf.position(tilew * imgYOffset + imgXOffset); + gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, + format, type, buf); + + /* Restore Alpha scale and bias */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + } + } else { + assert false; + return; + } + + if (pixelStore) { + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, 0); + } + + } + + void updateTextureFilterModes(Context ctx, + int target, + int minFilter, + int magFilter) { + GL gl = context(ctx).getGL(); + + if (EXTRA_DEBUGGING) { + System.err.println("minFilter: " + getFilterName(minFilter) + + " magFilter: " + getFilterName(magFilter)); + } + + // FIXME: unclear whether we really need to set up the enum values + // in the JoglContext as is done in the native code depending on + // extension availability; maybe this is the defined fallback + // behavior of the various Java3D modes + + // set texture min filter + switch (minFilter) { + case Texture.FASTEST: + case Texture.BASE_LEVEL_POINT: + gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + break; + case Texture.BASE_LEVEL_LINEAR: + gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + break; + case Texture.MULTI_LEVEL_POINT: + gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, + GL.GL_NEAREST_MIPMAP_NEAREST); + break; + case Texture.NICEST: + case Texture.MULTI_LEVEL_LINEAR: + gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, + GL.GL_LINEAR_MIPMAP_LINEAR); + break; + case Texture.FILTER4: +// We should never get here as we've disabled the FILTER4 feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, +// GL.GL_FILTER4_SGIS); + break; + } + + // set texture mag filter + switch (magFilter) { + case Texture.FASTEST: + case Texture.BASE_LEVEL_POINT: + gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + break; + case Texture.NICEST: + case Texture.BASE_LEVEL_LINEAR: + gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + break; + case Texture.LINEAR_SHARPEN: +// We should never get here as we've disabled the TEXTURE_SHARPEN feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_LINEAR_SHARPEN_SGIS); + break; + case Texture.LINEAR_SHARPEN_RGB: +// We should never get here as we've disabled the TEXTURE_SHARPEN feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_LINEAR_SHARPEN_COLOR_SGIS); + break; + case Texture.LINEAR_SHARPEN_ALPHA: +// We should never get here as we've disabled the TEXTURE_SHARPEN feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_LINEAR_SHARPEN_ALPHA_SGIS); + break; + case Texture2D.LINEAR_DETAIL: +// We should never get here as we've disabled the TEXTURE_DETAIL feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_LINEAR_DETAIL_SGIS); + break; + case Texture2D.LINEAR_DETAIL_RGB: +// We should never get here as we've disabled the TEXTURE_DETAIL feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_LINEAR_DETAIL_COLOR_SGIS); + break; + case Texture2D.LINEAR_DETAIL_ALPHA: +// We should never get here as we've disabled the TEXTURE_DETAIL feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_LINEAR_DETAIL_ALPHA_SGIS); + break; + case Texture.FILTER4: +// We should never get here as we've disabled the FILTER4 feature +// gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, +// GL.GL_FILTER4_SGIS); + break; + } + } + + void updateTextureBoundary(Context ctx, + int target, + int boundaryModeS, + int boundaryModeT, + int boundaryModeR, + float boundaryRed, + float boundaryGreen, + float boundaryBlue, + float boundaryAlpha) { + GL gl = context(ctx).getGL(); + + // set texture wrap parameter + switch (boundaryModeS) { + case Texture.WRAP: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); + break; + case Texture.CLAMP: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP); + break; + case Texture.CLAMP_TO_EDGE: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, + GL.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_BOUNDARY: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, + GL2.GL_CLAMP_TO_BORDER); + break; + } + + switch (boundaryModeT) { + case Texture.WRAP: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT); + break; + case Texture.CLAMP: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP); + break; + case Texture.CLAMP_TO_EDGE: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, + GL.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_BOUNDARY: + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, + GL2.GL_CLAMP_TO_BORDER); + break; + } + + // applies to Texture3D only + if (boundaryModeR != -1) { + switch (boundaryModeR) { + case Texture.WRAP: + gl.glTexParameteri(target, + GL2.GL_TEXTURE_WRAP_R, GL.GL_REPEAT); + break; + + case Texture.CLAMP: + gl.glTexParameteri(target, + GL2.GL_TEXTURE_WRAP_R, GL2.GL_CLAMP); + break; + case Texture.CLAMP_TO_EDGE: + gl.glTexParameteri(target, + GL2.GL_TEXTURE_WRAP_R, + GL.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_BOUNDARY: + gl.glTexParameteri(target, + GL2.GL_TEXTURE_WRAP_R, + GL2.GL_CLAMP_TO_BORDER); + break; + } + } + + if (boundaryModeS == Texture.CLAMP || + boundaryModeT == Texture.CLAMP || + boundaryModeR == Texture.CLAMP) { + // set texture border color + float[] color = new float[4]; + color[0] = boundaryRed; + color[1] = boundaryGreen; + color[2] = boundaryBlue; + color[3] = boundaryAlpha; + gl.glTexParameterfv(target, GL2.GL_TEXTURE_BORDER_COLOR, color, 0); + } + } + + private static final String getFilterName(int filter) { + switch (filter) { + case Texture.FASTEST: + return "Texture.FASTEST"; + case Texture.NICEST: + return "Texture.NICEST"; + case Texture.BASE_LEVEL_POINT: + return "Texture.BASE_LEVEL_POINT"; + case Texture.BASE_LEVEL_LINEAR: + return "Texture.BASE_LEVEL_LINEAR"; + case Texture.MULTI_LEVEL_POINT: + return "Texture.MULTI_LEVEL_POINT"; + case Texture.MULTI_LEVEL_LINEAR: + return "Texture.MULTI_LEVEL_LINEAR"; + case Texture.FILTER4: + return "Texture.FILTER4"; + case Texture.LINEAR_SHARPEN: + return "Texture.LINEAR_SHARPEN"; + case Texture.LINEAR_SHARPEN_RGB: + return "Texture.LINEAR_SHARPEN_RGB"; + case Texture.LINEAR_SHARPEN_ALPHA: + return "Texture.LINEAR_SHARPEN_ALPHA"; + case Texture2D.LINEAR_DETAIL: + return "Texture.LINEAR_DETAIL"; + case Texture2D.LINEAR_DETAIL_RGB: + return "Texture.LINEAR_DETAIL_RGB"; + case Texture2D.LINEAR_DETAIL_ALPHA: + return "Texture.LINEAR_DETAIL_ALPHA"; + default: + return "(unknown)"; + } + } + + private void updateTextureSharpenFunc(Context ctx, + int target, + int numPts, + float[] pts) { + // checking of the availability of sharpen texture functionality + // is already done in shared code +// FIXME: GL_SGIS_sharpen_texture +// GL gl = context(ctx).getGL(); +// gl.glSharpenTexFuncSGIS(target, numPts, pts, 0); + } + + private void updateTextureFilter4Func(Context ctx, + int target, + int numPts, + float[] pts) { + // checking of the availability of filter4 functionality + // is already done in shared code +// FIXME: GL_SGIS_texture_filter4 +// GL gl = context(ctx).getGL(); +// gl.glTexFilterFuncSGIS(target, GL.GL_FILTER4_SGIS, +// numPts, pts, 0); + } + + // mapping from java enum to gl enum + private static final int[] _gl_textureCubeMapFace = { + GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + }; + + // --------------------------------------------------------------------- + + // + // MasterControl methods + // + + // Maximum lights supported by the native API + @Override + int getMaximumLights() { + if (VERBOSE) System.err.println("JoglPipeline.getMaximumLights()"); + + // FIXME: this isn't quite what the NativePipeline returns but + // is probably close enough + return 8; + } + + + // --------------------------------------------------------------------- + + // + // Canvas3D methods - native wrappers + // + + // Mac/JRE 7; called from Renderer when resizing is dedected + // Implementation follows the approach in jogamp.opengl.GLDrawableHelper.resizeOffscreenDrawable(..) + @Override + void resizeOffscreenLayer(Canvas3D cv, int cvWidth, int cvHeight) { + if (!isOffscreenLayerSurfaceEnabled(cv)) + return; + + JoglDrawable joglDrawable = (JoglDrawable)cv.drawable; + if (!hasFBObjectSizeChanged(joglDrawable, cvWidth, cvHeight)) + return; + + int newWidth = Math.max(1, cvWidth); + int newHeight = Math.max(1, cvHeight); + + GLDrawable glDrawble = joglDrawable.getGLDrawable(); + GLContext glContext = context(cv.ctx); + + // Assuming glContext != null + + final NativeSurface surface = glDrawble.getNativeSurface(); + final ProxySurface proxySurface = (surface instanceof ProxySurface) ? (ProxySurface)surface : null; + + final int lockRes = surface.lockSurface(); + + try { + // propagate new size - seems not relevant here + if (proxySurface != null) { + final UpstreamSurfaceHook ush = proxySurface.getUpstreamSurfaceHook(); + if (ush instanceof UpstreamSurfaceHook.MutableSize) { + ((UpstreamSurfaceHook.MutableSize)ush).setSurfaceSize(newWidth, newHeight); + } + } + /*else if(DEBUG) { // we have to assume surface contains the new size already, hence size check @ bottom + System.err.println("GLDrawableHelper.resizeOffscreenDrawable: Drawable's offscreen surface n.a. ProxySurface, but "+ns.getClass().getName()+": "+ns); + }*/ + + GL2 gl = glContext.getGL().getGL2(); + + // FBO : should be the default case on Mac OS X + if (glDrawble instanceof GLFBODrawable) { + + // Resize GLFBODrawable + // TODO msaa gets lost +// ((GLFBODrawable)glDrawble).resetSize(gl); + + // Alternative: resize GL_BACK FBObject directly, + // if multisampled the FBO sink (GL_FRONT) will be resized before the swap is executed + int numSamples = ((GLFBODrawable)glDrawble).getChosenGLCapabilities().getNumSamples(); + FBObject fboObjectBack = ((GLFBODrawable)glDrawble).getFBObject( GL.GL_BACK ); + fboObjectBack.reset(gl, newWidth, newHeight, numSamples/*, false*/); // false = don't reset SamplingSinkFBO immediately + fboObjectBack.bind(gl); + + // If double buffered without antialiasing the GL_FRONT FBObject + // will be resized by glDrawble after the next swap-call + } + // pbuffer - not tested because Mac OS X 10.7+ supports FBO + else { + // Create new GLDrawable (pbuffer) and update the coresponding GLContext + + final GLContext currentContext = GLContext.getCurrent(); + final GLDrawableFactory factory = glDrawble.getFactory(); + + // Ensure to sync GL command stream + if (currentContext != glContext) { + glContext.makeCurrent(); + } + gl.glFinish(); + glContext.release(); + + if (proxySurface != null) { + proxySurface.enableUpstreamSurfaceHookLifecycle(false); + } + + try { + glDrawble.setRealized(false); + // New GLDrawable + glDrawble = factory.createGLDrawable(surface); + glDrawble.setRealized(true); + + joglDrawable.setGLDrawable(glDrawble); + } + finally { + if (proxySurface != null) { + proxySurface.enableUpstreamSurfaceHookLifecycle(true); + } + } + + glContext.setGLDrawable(glDrawble, true); // re-association + + // make current last current context + if (currentContext != null) { + currentContext.makeCurrent(); + } + } + } + finally { + surface.unlockSurface(); + } + } + + // Fix for Bug 983 + private void checkAppContext() { + if (mainThreadContext == null) + return; + + try { + // Check by reflection that sun.awt.AppContext.getAppContext() doesn't return null + // (required by ImageIO.write() and other JMF internal calls) to apply workaround proposed at + // http://stackoverflow.com/questions/17223304/appcontext-is-null-from-rmi-thread-with-java-7-update-25 + final Class appContextClass = Class.forName("sun.awt.AppContext"); + if (appContextClass.getMethod("getAppContext").invoke(null) == null) { + final Field field = appContextClass.getDeclaredField("threadGroup2appContext"); + field.setAccessible(true); + final Map threadGroup2appContext = (Map)field.get(null); + final ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup(); + threadGroup2appContext.put(currentThreadGroup, mainThreadContext); + } + } catch (Throwable ex) { + // Let's consider app context is not necessary for the program + } + // Don't need mainThreadContext anymore + mainThreadContext = null; + } + + // This is the native method for creating the underlying graphics context. + @Override + Context createNewContext(Canvas3D cv, Drawable drawable, + Context shareCtx, boolean isSharedCtx, + boolean offScreen) { + if (VERBOSE) System.err.println("JoglPipeline.createNewContext()"); + + checkAppContext(); + GLDrawable glDrawable = null; + GLContext glContext = null; + + if (offScreen) { + glDrawable = drawable(cv.drawable); // cv.drawable != null, set in 'createOffScreenBuffer' + glContext = glDrawable.createContext(context(shareCtx)); + } + else { + // determined in 'getBestConfiguration' + GraphicsConfigInfo gcInf0 = Canvas3D.graphicsConfigTable.get(cv.graphicsConfiguration); + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration)gcInf0.getPrivateData(); + + // JAWTWindow + JAWTWindow nativeWindow = (JAWTWindow)NativeWindowFactory.getNativeWindow(cv, awtConfig); + nativeWindow.lockSurface(); + try { + glDrawable = GLDrawableFactory.getFactory(profile).createGLDrawable(nativeWindow); + glContext = glDrawable.createContext(context(shareCtx)); + } + finally { + nativeWindow.unlockSurface(); + } + + cv.drawable = new JoglDrawable(glDrawable, nativeWindow); + } + + // assuming that this only gets called after addNotify has been called + glDrawable.setRealized(true); + + // Apparently we are supposed to make the context current at this point + // and set up a bunch of properties + + // Work around for some low end graphics driver bug, such as Intel Chipset. + // Issue 324 : Lockup J3D program and throw exception using JOGL renderer + boolean failed = false; + int failCount = 0; + int MAX_FAIL_COUNT = 5; + do { + failed = false; + int res = glContext.makeCurrent(); + if (res == GLContext.CONTEXT_NOT_CURRENT) { + // System.err.println("makeCurrent fail : " + failCount); + failed = true; + ++failCount; + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + } + } + } while (failed && (failCount < MAX_FAIL_COUNT)); + + if (failCount == MAX_FAIL_COUNT) { + throw new IllegalRenderingStateException("Unable to make new context current after " + failCount + "tries"); + } + + GL2 gl = glContext.getGL().getGL2(); + + JoglContext ctx = new JoglContext(glContext); + + try { + if (!getPropertiesFromCurrentContext(ctx, gl)) { + throw new IllegalRenderingStateException("Unable to fetch properties from current OpenGL context"); + } + + if(!isSharedCtx){ + // Set up fields in Canvas3D + setupCanvasProperties(cv, ctx, gl); + } + + // Enable rescale normal + gl.glEnable(GL2.GL_RESCALE_NORMAL); + + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE); + gl.glDepthFunc(GL.GL_LEQUAL); + gl.glEnable(GL2.GL_COLOR_MATERIAL); + + /* + OpenGL specs: + glReadBuffer specifies a color buffer as the source for subsequent glReadPixels. + This source mode is initially GL_FRONT in single-buffered and GL_BACK in double-buffered configurations. + + We leave this mode unchanged in on-screen rendering and adjust it in off-screen rendering. See below. + */ +// gl.glReadBuffer(GL_FRONT); // off window, default for single-buffered non-stereo window + + // Issue 417: JOGL: Mip-mapped NPOT textures rendered incorrectly + // J3D images are aligned to 1 byte + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); + + // Workaround for issue 400: Enable separate specular by default + gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, GL2.GL_SEPARATE_SPECULAR_COLOR); + + + // Mac OS X / JRE 7 : onscreen rendering = offscreen rendering + // bind FBO + if (!offScreen && glDrawable instanceof GLFBODrawable) { + GLFBODrawable fboDrawable = (GLFBODrawable)glDrawable; + // bind GLFBODrawable's drawing FBObject + // GL_BACK returns the correct FBOObject for single/double buffering, incl. multisampling + fboDrawable.getFBObject( GL.GL_BACK ).bind(gl); + } + + + // FBO or pbuffer + if (offScreen) { + + // Final caps + GLCapabilitiesImmutable chosenCaps = glDrawable.getChosenGLCapabilities(); + + // FBO + if (glDrawable instanceof GLFBODrawable) { + GLFBODrawable fboDrawable = (GLFBODrawable)glDrawable; + // bind GLFBODrawable's drawing FBObject + // GL_BACK returns the correct FBOObject for single/double buffering, incl. multisampling + fboDrawable.getFBObject( GL.GL_BACK ).bind(gl); + } + // pbuffer + else { + // Double buffering: read from back buffer, as we don't swap + // Even this setting is identical to the initially mode it is set explicitly + if (chosenCaps.getDoubleBuffered()) { + gl.glReadBuffer(GL.GL_BACK); + } + else { + gl.glReadBuffer(GL.GL_FRONT); + } + } + } + } + finally { + glContext.release(); + } + + return ctx; + } + + @Override + void createQueryContext(Canvas3D cv, Drawable drawable, + boolean offScreen, int width, int height) { + if (VERBOSE) System.err.println("JoglPipeline.createQueryContext()"); + + // Assumes createQueryContext is never called for a drawable != null + + if (offScreen) { + + Drawable offDrawable = createOffScreenBuffer(cv, null, width, height); + + GLDrawable glDrawable = drawable(offDrawable); + + glDrawable.setRealized(true); + + GLContext glContext = glDrawable.createContext(null); + glContext.makeCurrent(); + + JoglContext ctx = new JoglContext(glContext); + + GL2 gl = glContext.getGL().getGL2(); + + // get current context properties + getPropertiesFromCurrentContext(ctx, gl); + // Set up fields in Canvas3D + setupCanvasProperties(cv, ctx, gl); + + // Done ! + + glContext.release(); + glContext.destroy(); + glDrawable.setRealized(false); + } + else { + + // TODO can't find an implementation which avoids the use of QueryCanvas + // JOGL requires a visible Frame for an onscreen context + + Frame f = new Frame(); + f.setUndecorated(true); + f.setLayout(new BorderLayout()); + + ContextQuerier querier = new ContextQuerier(cv); + + AWTGraphicsConfiguration awtConfig = + (AWTGraphicsConfiguration)Canvas3D.graphicsConfigTable.get(cv.graphicsConfiguration).getPrivateData(); + + QueryCanvas canvas = new QueryCanvas(awtConfig, querier); + + f.add(canvas, BorderLayout.CENTER); + f.setSize(MIN_FRAME_SIZE, MIN_FRAME_SIZE); + f.setVisible(true); + canvas.doQuery(); + // Attempt to wait for the frame to become visible, but don't block the EDT + if (!EventQueue.isDispatchThread()) { + synchronized(querier) { + if (!querier.done()) { + try { + querier.wait(WAIT_TIME); + } + catch (InterruptedException e) { + } + } + } + } + + disposeOnEDT(f); + } + } + + // This is the native for creating an offscreen buffer + @Override + Drawable createOffScreenBuffer(Canvas3D cv, Context ctx, int width, int height) { + if (VERBOSE) System.err.println("JoglPipeline.createOffScreenBuffer()"); + + // ctx unused, doesn't exist yet + + // Offscreen Canvas3D's JoglGraphicsConfiguration + JoglGraphicsConfiguration jgc = (JoglGraphicsConfiguration)cv.graphicsConfiguration; + + // Retrieve the offscreen Canvas3D's GraphicsConfigInfo + GraphicsConfigInfo gcInf0 = Canvas3D.graphicsConfigTable.get(jgc); + + // Offscreen Canvas3D's graphics configuration, determined in 'getBestConfiguration' + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration)gcInf0.getPrivateData(); + + // TODO Offscreen Canvas3D's graphics devise, determined in 'getBestConfiguration' + //AbstractGraphicsDevice device = awtConfig.getScreen().getDevice(); // throws exception + // Alternative: default graphics device + AbstractGraphicsDevice device = GLDrawableFactory.getDesktopFactory().getDefaultDevice(); + + // Offscreen Canvas3D's capabilites, determined in 'getBestConfiguration' + GLCapabilities canvasCaps = (GLCapabilities)awtConfig.getChosenCapabilities(); + + // For further investigations : the user's GraphicsConfigTemplate3D (not used yet) + GraphicsConfigTemplate3D gct3D = gcInf0.getGraphicsConfigTemplate3D(); + + + // Assuming that the offscreen drawable will/can support the chosen GLCapabilities + // of the offscreen Canvas3D + + final GLCapabilities offCaps = new GLCapabilities(profile); + offCaps.copyFrom(canvasCaps); + + // double bufffering only if scene antialiasing is required/preferred and supported + if (offCaps.getSampleBuffers() == false) { + offCaps.setDoubleBuffered(false); + offCaps.setNumSamples(0); + } + + // Never stereo + offCaps.setStereo(false); + + // Set preferred offscreen drawable : framebuffer object (FBO) or pbuffer + offCaps.setFBO(true); // switches to pbuffer if FBO is not supported + // caps.setPBuffer(true); + + // !! a 'null' capability chooser; JOGL doesn't call a chooser for offscreen drawable + + // If FBO : 'offDrawable' is of type com.jogamp.opengl.GLFBODrawable + GLDrawable offDrawable = GLDrawableFactory.getFactory(profile).createOffscreenDrawable(device, offCaps, null, width, height); + +// !! these chosen caps are not final as long as the corresponding context is made current +//System.out.println("createOffScreenBuffer chosenCaps = " + offDrawable.getChosenGLCapabilities()); + + return new JoglDrawable(offDrawable, null); + } + + // 'destroyContext' is called first if context exists + @Override + void destroyOffScreenBuffer(Canvas3D cv, Context ctx, Drawable drawable) { + if (VERBOSE) System.err.println("JoglPipeline.destroyOffScreenBuffer()"); + + // it is done in 'destroyContext' + } + + // This is the native for reading the image from the offscreen buffer + @Override + void readOffScreenBuffer(Canvas3D cv, Context ctx, int format, int dataType, Object data, int width, int height) { + if (VERBOSE) System.err.println("JoglPipeline.readOffScreenBuffer()"); + + GLDrawable glDrawable = ((JoglDrawable)cv.drawable).getGLDrawable(); + GLCapabilitiesImmutable chosenCaps = glDrawable.getChosenGLCapabilities(); + GLFBODrawable fboDrawable = null; + + GL2 gl = context(ctx).getGL().getGL2(); + + // If FBO + if (chosenCaps.isFBO()) { + + fboDrawable = (GLFBODrawable)glDrawable; + + if (chosenCaps.getDoubleBuffered()) { + // swap = resolve multisampling or flip back/front FBO + fboDrawable.swapBuffers(); + // unbind texture render target, we read from FBO + gl.glBindTexture(GL.GL_TEXTURE_2D, 0); + } + + // bind FBO for reading pixel data + // GL_FRONT = SamplingSinkFBO if double buffered and multisampled + // GL_FRONT if double buffered ( = GL_BAck before swap was called) + // GL_FRONT = GL_BACK if single buffered (single FBO) + + fboDrawable.getFBObject( GL.GL_FRONT ).bind(gl); + } + // else pbuffer + + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, width); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); + + int type = 0; + + if((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) { + + switch (format) { + // GL_BGR + case ImageComponentRetained.TYPE_BYTE_BGR: + type = GL2.GL_BGR; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + type = GL.GL_RGB; + break; + // GL_ABGR_EXT + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) { // If false, should never come here! + type = GL2.GL_ABGR_EXT; + } else { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + type = GL.GL_RGBA; + break; + + /* This method only supports 3 and 4 components formats and BYTE types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + throw new AssertionError("illegal format " + format); + } + + gl.glReadPixels(0, 0, width, height, type, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap((byte[]) data)); + + } + else if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) || + (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) { + + int intType = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + + switch (format) { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + type = GL.GL_RGBA; + intType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + type = GL2.GL_BGRA; + intType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* This method only supports 3 and 4 components formats and BYTE types. */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + throw new AssertionError("illegal format " + format); + } + + /* Force Alpha to 1.0 if needed */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + } + + gl.glReadPixels(0, 0, width, height, type, intType, IntBuffer.wrap((int[]) data)); + + /* Restore Alpha scale and bias */ + if(forceAlphaToOne) { + gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + } + } + else { + throw new AssertionError("illegal image data type " + dataType); + } + + // If FBO + if (chosenCaps.isFBO()) { + // bind FBO for drawing + fboDrawable.getFBObject( GL.GL_BACK ).bind(gl); + } + } + + // The native method for swapBuffers - onscreen only +@Override +void swapBuffers(Canvas3D cv, Context ctx, Drawable drawable) { + if (VERBOSE) System.err.println("JoglPipeline.swapBuffers()"); + GLDrawable draw = drawable(drawable); + draw.swapBuffers(); +} + + // native method for setting Material when no material is present + @Override + void updateMaterialColor(Context ctx, float r, float g, float b, float a) { + if (VERBOSE) System.err.println("JoglPipeline.updateMaterialColor()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glColor4f(r, g, b, a); + gl.glDisable(GL2.GL_LIGHTING); + } + + @Override + void destroyContext(Drawable drawable, Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.destroyContext()"); + + JoglDrawable joglDrawable = (JoglDrawable)drawable; + GLContext context = context(ctx); + + if (GLContext.getCurrent() == context) { + context.release(); + } + context.destroy(); + + // assuming this is the right point at which to make this call + joglDrawable.getGLDrawable().setRealized(false); + + joglDrawable.destroyNativeWindow(); + } + + // This is the native method for doing accumulation. + @Override + void accum(Context ctx, float value) { + if (VERBOSE) System.err.println("JoglPipeline.accum()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glReadBuffer(GL.GL_BACK); + gl.glAccum(GL2.GL_ACCUM, value); + gl.glReadBuffer(GL.GL_FRONT); + } + + // This is the native method for doing accumulation return. + @Override + void accumReturn(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.accumReturn()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glAccum(GL2.GL_RETURN, 1.0f); + } + + // This is the native method for clearing the accumulation buffer. + @Override + void clearAccum(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.clearAccum()"); + + GL gl = context(ctx).getGL(); + gl.glClear(GL2.GL_ACCUM_BUFFER_BIT); + } + + // This is the native method for getting the number of lights the underlying + // native library can support. + @Override + int getNumCtxLights(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.getNumCtxLights()"); + + GL gl = context(ctx).getGL(); + int[] res = new int[1]; + gl.glGetIntegerv(GL2.GL_MAX_LIGHTS, res, 0); + return res[0]; + } + + // Native method for decal 1st child setup + @Override + boolean decal1stChildSetup(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.decal1stChildSetup()"); + + GL gl = context(ctx).getGL(); + gl.glEnable(GL.GL_STENCIL_TEST); + gl.glClearStencil(0x0); + gl.glClear(GL.GL_STENCIL_BUFFER_BIT); + gl.glStencilFunc(GL.GL_ALWAYS, 0x1, 0x1); + gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_REPLACE); + if (gl.glIsEnabled(GL.GL_DEPTH_TEST)) + return true; + else + return false; + } + + // Native method for decal nth child setup + @Override + void decalNthChildSetup(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.decalNthChildSetup()"); + + GL gl = context(ctx).getGL(); + gl.glDisable(GL.GL_DEPTH_TEST); + gl.glStencilFunc(GL.GL_EQUAL, 0x1, 0x1); + gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP); + } + + // Native method for decal reset + @Override + void decalReset(Context ctx, boolean depthBufferEnable) { + if (VERBOSE) System.err.println("JoglPipeline.decalReset()"); + + GL gl = context(ctx).getGL(); + gl.glDisable(GL.GL_STENCIL_TEST); + if (depthBufferEnable) + gl.glEnable(GL.GL_DEPTH_TEST); + } + + // Native method for eye lighting + @Override + void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable) { + if (VERBOSE) System.err.println("JoglPipeline.ctxUpdateEyeLightingEnable()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (localEyeLightingEnable) { + gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE); + } else { + gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_FALSE); + } + } + + // The following three methods are used in multi-pass case + + // native method for setting blend color + @Override + void setBlendColor(Context ctx, float red, float green, + float blue, float alpha) { + if (VERBOSE) System.err.println("JoglPipeline.setBlendColor()"); + + GL2 gl = context(ctx).getGL().getGL2(); + if (gl.isExtensionAvailable("GL_ARB_imaging")) { + gl.glBlendColor(red, green, blue, alpha); + } + } + + // native method for setting blend func + @Override + void setBlendFunc(Context ctx, int srcBlendFunction, int dstBlendFunction) { + if (VERBOSE) System.err.println("JoglPipeline.setBlendFunc()"); + + GL gl = context(ctx).getGL(); + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(blendFunctionTable[srcBlendFunction], + blendFunctionTable[dstBlendFunction]); + } + + // native method for setting fog enable flag + @Override + void setFogEnableFlag(Context ctx, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.setFogEnableFlag()"); + + GL gl = context(ctx).getGL(); + + if (enable) + gl.glEnable(GL2.GL_FOG); + else + gl.glDisable(GL2.GL_FOG); + } + + // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported + @Override + void setFullSceneAntialiasing(Context absCtx, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.setFullSceneAntialiasing()"); + + JoglContext ctx = (JoglContext) absCtx; + GL gl = context(ctx).getGL(); + if (ctx.getHasMultisample() && !VirtualUniverse.mc.implicitAntialiasing) { + if (enable) { + gl.glEnable(GL.GL_MULTISAMPLE); + } else { + gl.glDisable(GL.GL_MULTISAMPLE); + } + } + } + + // Native method to update separate specular color control + @Override + void updateSeparateSpecularColorEnable(Context ctx, boolean enable) { + if (VERBOSE) System.err.println("JoglPipeline.updateSeparateSpecularColorEnable()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (enable) { + gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, GL2.GL_SEPARATE_SPECULAR_COLOR); + } else { + gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, GL2.GL_SINGLE_COLOR); + } + } + + // True under Solaris, + // False under windows when display mode <= 8 bit + @Override + boolean validGraphicsMode() { + if (VERBOSE) System.err.println("JoglPipeline.validGraphicsMode()"); + + // FIXME: believe this should do exactly what the native code + // used to, but not 100% sure (also in theory should only run + // this code on the Windows platform? What about Mac OS X?) + DisplayMode currentMode = + GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); + // Note: on X11 platforms, a bit depth < 0 simply indicates that + // multiple visuals are supported on the current display mode + + if (VERBOSE) System.err.println(" Returning " + (currentMode.getBitDepth() < 0 || + currentMode.getBitDepth() > 8)); + + return (currentMode.getBitDepth() < 0 || + currentMode.getBitDepth() > 8); + } + + // native method for setting light enables + @Override + void setLightEnables(Context ctx, long enableMask, int maxLights) { + if (VERBOSE) System.err.println("JoglPipeline.setLightEnables()"); + + GL gl = context(ctx).getGL(); + + for (int i = 0; i < maxLights; i++) { + if ((enableMask & (1 << i)) != 0) { + gl.glEnable(GL2.GL_LIGHT0 + i); + } else { + gl.glDisable(GL2.GL_LIGHT0 + i); + } + } + } + + // native method for setting scene ambient + @Override + void setSceneAmbient(Context ctx, float red, float green, float blue) { + if (VERBOSE) System.err.println("JoglPipeline.setSceneAmbient()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + float[] color = new float[4]; + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = 1.0f; + gl.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, color, 0); + } + + // native method for disabling fog + @Override + void disableFog(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.disableFog()"); + + GL gl = context(ctx).getGL(); + gl.glDisable(GL2.GL_FOG); + } + + // native method for disabling modelClip + @Override + void disableModelClip(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.disableModelClip()"); + + GL gl = context(ctx).getGL(); + + gl.glDisable(GL2.GL_CLIP_PLANE0); + gl.glDisable(GL2.GL_CLIP_PLANE1); + gl.glDisable(GL2.GL_CLIP_PLANE2); + gl.glDisable(GL2.GL_CLIP_PLANE3); + gl.glDisable(GL2.GL_CLIP_PLANE4); + gl.glDisable(GL2.GL_CLIP_PLANE5); + } + + // native method for setting default RenderingAttributes + @Override + void resetRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride) { + if (VERBOSE) System.err.println("JoglPipeline.resetRenderingAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (!depthBufferWriteEnableOverride) { + gl.glDepthMask(true); + } + if (!depthBufferEnableOverride) { + gl.glEnable(GL.GL_DEPTH_TEST); + } + gl.glAlphaFunc(GL.GL_ALWAYS, 0.0f); + gl.glDepthFunc(GL.GL_LEQUAL); + gl.glEnable(GL2.GL_COLOR_MATERIAL); + gl.glDisable(GL.GL_COLOR_LOGIC_OP); + } + + // native method for setting default texture + @Override + void resetTextureNative(Context ctx, int texUnitIndex) { + if (VERBOSE) System.err.println("JoglPipeline.resetTextureNative()"); + + GL2 gl = context(ctx).getGL().getGL2(); + if (texUnitIndex >= 0 && + gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glActiveTexture(texUnitIndex + GL.GL_TEXTURE0); + gl.glClientActiveTexture(texUnitIndex + GL.GL_TEXTURE0); + } + + gl.glDisable(GL2.GL_TEXTURE_1D); + gl.glDisable(GL.GL_TEXTURE_2D); + gl.glDisable(GL2.GL_TEXTURE_3D); + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + } + + // native method for activating a particular texture unit + @Override + void activeTextureUnit(Context ctx, int texUnitIndex) { + if (VERBOSE) System.err.println("JoglPipeline.activeTextureUnit()"); + + GL2 gl = context(ctx).getGL().getGL2(); + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glActiveTexture(texUnitIndex + GL.GL_TEXTURE0); + gl.glClientActiveTexture(texUnitIndex + GL.GL_TEXTURE0); + } + } + + // native method for setting default TexCoordGeneration + @Override + void resetTexCoordGeneration(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.resetTexCoordGeneration()"); + + GL gl = context(ctx).getGL(); + gl.glDisable(GL2.GL_TEXTURE_GEN_S); + gl.glDisable(GL2.GL_TEXTURE_GEN_T); + gl.glDisable(GL2.GL_TEXTURE_GEN_R); + gl.glDisable(GL2.GL_TEXTURE_GEN_Q); + } + + // native method for setting default TextureAttributes + @Override + void resetTextureAttributes(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.resetTextureAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + float[] color = new float[4]; + + gl.glPushAttrib(GL2.GL_TRANSFORM_BIT); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glLoadIdentity(); + gl.glPopAttrib(); + gl.glTexEnvfv(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_COLOR, color, 0); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); + gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); + +// FIXME: GL_NV_register_combiners +// if (gl.isExtensionAvailable("GL_NV_register_combiners")) { +// gl.glDisable(GL.GL_REGISTER_COMBINERS_NV); +// } + +// FIXME: GL_SGI_texture_color_table +// if (gl.isExtensionAvailable("GL_SGI_texture_color_table")) { +// gl.glDisable(GL.GL_TEXTURE_COLOR_TABLE_SGI); +// } + } + + // native method for setting default PolygonAttributes + @Override + void resetPolygonAttributes(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.resetPolygonAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + gl.glCullFace(GL.GL_BACK); + gl.glEnable(GL.GL_CULL_FACE); + + gl.glLightModeli(GL2.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_FALSE); + + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL); + + gl.glPolygonOffset(0.0f, 0.0f); + gl.glDisable(GL2.GL_POLYGON_OFFSET_POINT); + gl.glDisable(GL2.GL_POLYGON_OFFSET_LINE); + gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); + } + + // native method for setting default LineAttributes + @Override + void resetLineAttributes(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.resetLineAttributes()"); + + GL gl = context(ctx).getGL(); + gl.glLineWidth(1.0f); + gl.glDisable(GL2.GL_LINE_STIPPLE); + + // XXXX: Polygon Mode check, blend enable + gl.glDisable(GL.GL_LINE_SMOOTH); + } + + // native method for setting default PointAttributes + @Override + void resetPointAttributes(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.resetPointAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glPointSize(1.0f); + + // XXXX: Polygon Mode check, blend enable + gl.glDisable(GL2.GL_POINT_SMOOTH); + } + + // native method for setting default TransparencyAttributes + @Override + void resetTransparency(Context ctx, int geometryType, + int polygonMode, boolean lineAA, + boolean pointAA) { + if (VERBOSE) System.err.println("JoglPipeline.resetTransparency()"); + + GL gl = context(ctx).getGL(); + + if (((((geometryType & RenderMolecule.LINE) != 0) || + (polygonMode == PolygonAttributes.POLYGON_LINE)) + && lineAA) || + ((((geometryType & RenderMolecule.POINT) != 0) || + (polygonMode == PolygonAttributes.POLYGON_POINT)) + && pointAA)) { + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + } else { + gl.glDisable(GL.GL_BLEND); + } + gl.glDisable(GL2.GL_POLYGON_STIPPLE); + } + + // native method for setting default ColoringAttributes + @Override + void resetColoringAttributes(Context ctx, + float r, float g, + float b, float a, + boolean enableLight) { + if (VERBOSE) System.err.println("JoglPipeline.resetColoringAttributes()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + if (!enableLight) { + gl.glColor4f(r, g, b, a); + } + gl.glShadeModel(GL2.GL_SMOOTH); + } + + /** + * This native method makes sure that the rendering for this canvas + * gets done now. + */ + @Override + void syncRender(Context ctx, boolean wait) { + if (VERBOSE) System.err.println("JoglPipeline.syncRender()"); + + GL gl = context(ctx).getGL(); + + if (wait) + gl.glFinish(); + else + gl.glFlush(); + } + + // The native method that sets this ctx to be the current one + @Override + boolean useCtx(Context ctx, Drawable drawable) { + if (VERBOSE) System.err.println("JoglPipeline.useCtx()"); + GLContext context = context(ctx); + int res = context.makeCurrent(); + return (res != GLContext.CONTEXT_NOT_CURRENT); + } + + // Optionally release the context. Returns true if the context was released. + @Override + boolean releaseCtx(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.releaseCtx()"); + GLContext context = context(ctx); + context.release(); + return true; + } + + @Override + void clear(Context ctx, float r, float g, float b, boolean clearStencil) { + if (VERBOSE) System.err.println("JoglPipeline.clear()"); + + JoglContext jctx = (JoglContext) ctx; + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + // OBSOLETE CLEAR CODE + /* + gl.glClearColor(r, g, b, jctx.getAlphaClearValue()); + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + + // Java 3D always clears the Z-buffer + gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT); + gl.glDepthMask(true); + gl.glClear(GL.GL_DEPTH_BUFFER_BIT); + gl.glPopAttrib(); + + // Issue 239 - clear stencil if specified + if (clearStencil) { + gl.glPushAttrib(GL.GL_STENCIL_BUFFER_BIT); + gl.glClearStencil(0); + gl.glStencilMask(~0); + gl.glClear(GL.GL_STENCIL_BUFFER_BIT); + gl.glPopAttrib(); + } + */ + + // Mask of which buffers to clear, this always includes color & depth + int clearMask = GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT; + + // Issue 239 - clear stencil if specified + if (clearStencil) { + gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT); + + gl.glClearStencil(0); + gl.glStencilMask(~0); + clearMask |= GL.GL_STENCIL_BUFFER_BIT; + } else { + gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT); + } + + gl.glDepthMask(true); + gl.glClearColor(r, g, b, jctx.getAlphaClearValue()); + gl.glClear(clearMask); + gl.glPopAttrib(); + + } + + @Override + void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, boolean useBilinearFilter) { + if (VERBOSE) System.err.println("JoglPipeline.textureFillBackground()"); + + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + // Temporarily disable fragment and most 3D operations + gl.glPushAttrib(GL2.GL_ENABLE_BIT | GL2.GL_TEXTURE_BIT | GL2.GL_POLYGON_BIT); + + disableAttribFor2D(gl); + gl.glDepthMask(false); + gl.glEnable(GL.GL_TEXTURE_2D); + + /* Setup filter mode if needed */ + if(useBilinearFilter) { + // System.err.println("JoglPipeline - Background : use bilinear filter\n"); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + } + + // reset the polygon mode + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL); + + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); + + // load identity modelview and projection matrix + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glLoadIdentity(); + + gl.glBegin(GL2.GL_QUADS); + gl.glTexCoord2f(texMinU, texMinV); gl.glVertex2f(mapMinX,mapMinY); + gl.glTexCoord2f(texMaxU, texMinV); gl.glVertex2f(mapMaxX,mapMinY); + gl.glTexCoord2f(texMaxU, texMaxV); gl.glVertex2f(mapMaxX,mapMaxY); + gl.glTexCoord2f(texMinU, texMaxV); gl.glVertex2f(mapMinX,mapMaxY); + gl.glEnd(); + + // Restore texture Matrix transform + gl.glPopMatrix(); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + // Restore attributes + gl.glPopAttrib(); + + } + + @Override + void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, float mapZ, float alpha, + boolean useBilinearFilter) { + + if (VERBOSE) System.err.println("JoglPipeline.textureFillRaster()"); + + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + // Temporarily disable fragment and most 3D operations + gl.glPushAttrib(GL2.GL_ENABLE_BIT | GL2.GL_TEXTURE_BIT | GL2.GL_POLYGON_BIT | + GL2.GL_CURRENT_BIT ); + + disableAttribForRaster(gl); + + /* Setup filter mode if needed */ + if(useBilinearFilter) { + // System.err.println("JoglPipeline - Raster : use bilinear filter\n"); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + } + + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE); + gl.glColor4f(1.0f, 1.0f, 1.0f, alpha); + + // reset the polygon mode + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL); + + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); + + // load identity modelview and projection matrix + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0); + + gl.glBegin(GL2.GL_QUADS); + gl.glTexCoord2f(texMinU, texMinV); gl.glVertex3f(mapMinX,mapMinY, mapZ); + gl.glTexCoord2f(texMaxU, texMinV); gl.glVertex3f(mapMaxX,mapMinY, mapZ); + gl.glTexCoord2f(texMaxU, texMaxV); gl.glVertex3f(mapMaxX,mapMaxY, mapZ); + gl.glTexCoord2f(texMinU, texMaxV); gl.glVertex3f(mapMinX,mapMaxY, mapZ); + gl.glEnd(); + + // Restore matrices + gl.glPopMatrix(); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glPopMatrix(); + // Restore attributes + gl.glPopAttrib(); + + } + + @Override + void executeRasterDepth(Context ctx, float posX, float posY, float posZ, + int srcOffsetX, int srcOffsetY, int rasterWidth, int rasterHeight, + int depthWidth, int depthHeight, int depthFormat, Object depthData) { + if (VERBOSE) System.err.println("JoglPipeline.executeRasterDepth()"); + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + + gl.glRasterPos3f(posX, posY, posZ); + + int[] drawBuf = new int[1]; + gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuf, 0); + /* disable draw buffer */ + gl.glDrawBuffer(GL.GL_NONE); + + /* + * raster position is upper left corner, default for Java3D + * ImageComponent currently has the data reverse in Y + */ + gl.glPixelZoom(1.0f, -1.0f); + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, depthWidth); + if (srcOffsetX >= 0) { + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, srcOffsetX); + if (srcOffsetX + rasterWidth > depthWidth) { + rasterWidth = depthWidth - srcOffsetX; + } + } else { + rasterWidth += srcOffsetX; + if (rasterWidth > depthWidth) { + rasterWidth = depthWidth; + } + } + if (srcOffsetY >= 0) { + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, srcOffsetY); + if (srcOffsetY + rasterHeight > depthHeight) { + rasterHeight = depthHeight - srcOffsetY; + } + } else { + rasterHeight += srcOffsetY; + if (rasterHeight > depthHeight) { + rasterHeight = depthHeight; + } + } + + + if (depthFormat == DepthComponentRetained.DEPTH_COMPONENT_TYPE_INT) { + gl.glDrawPixels(rasterWidth, rasterHeight, GL2.GL_DEPTH_COMPONENT, + GL.GL_UNSIGNED_INT, IntBuffer.wrap((int[]) depthData)); + } else { /* DepthComponentRetained.DEPTH_COMPONENT_TYPE_FLOAT */ + gl.glDrawPixels(rasterWidth, rasterHeight, GL2.GL_DEPTH_COMPONENT, + GL.GL_FLOAT, FloatBuffer.wrap((float[]) depthData)); + } + + /* re-enable draw buffer */ + gl.glDrawBuffer(drawBuf[0]); + + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, 0); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, 0); + + } + + // The native method for setting the ModelView matrix. + @Override + void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix) { + if (VERBOSE) System.err.println("JoglPipeline.setModelViewMatrix()"); + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + gl.glLoadTransposeMatrixd(viewMatrix, 0); + gl.glMultTransposeMatrixd(modelMatrix, 0); + } else { + double[] v = new double[16]; + double[] m = new double[16]; + copyTranspose(viewMatrix, v); + copyTranspose(modelMatrix, m); + gl.glLoadMatrixd(v, 0); + gl.glMultMatrixd(m, 0); + } + } + + // The native method for setting the Projection matrix. + @Override + void setProjectionMatrix(Context ctx, double[] projMatrix) { + if (VERBOSE) System.err.println("JoglPipeline.setProjectionMatrix()"); + GLContext context = context(ctx); + GL2 gl = context.getGL().getGL2(); + + gl.glMatrixMode(GL2.GL_PROJECTION); + + if (gl.isExtensionAvailable("GL_VERSION_1_3")) { + // Invert the Z value in clipping coordinates because OpenGL uses + // left-handed clipping coordinates, while Java3D defines right-handed + // coordinates everywhere. + projMatrix[8] *= -1.0; + projMatrix[9] *= -1.0; + projMatrix[10] *= -1.0; + projMatrix[11] *= -1.0; + gl.glLoadTransposeMatrixd(projMatrix, 0); + projMatrix[8] *= -1.0; + projMatrix[9] *= -1.0; + projMatrix[10] *= -1.0; + projMatrix[11] *= -1.0; + } else { + double[] p = new double[16]; + copyTranspose(projMatrix, p); + // Invert the Z value in clipping coordinates because OpenGL uses + // left-handed clipping coordinates, while Java3D defines right-handed + // coordinates everywhere. + p[2] *= -1.0; + p[6] *= -1.0; + p[10] *= -1.0; + p[14] *= -1.0; + gl.glLoadMatrixd(p, 0); + } + } + +static boolean isOffscreenLayerSurfaceEnabled(Canvas3D cv) { + if (cv.drawable == null || cv.offScreen) + return false; + + JoglDrawable joglDrawble = (JoglDrawable)cv.drawable; + JAWTWindow jawtwindow = (JAWTWindow)joglDrawble.getNativeWindow(); + if (jawtwindow == null) + return false; + + return jawtwindow.isOffscreenLayerSurfaceEnabled(); +} + +static boolean hasFBObjectSizeChanged(JoglDrawable jdraw, int width, int height) { + if (!(jdraw.getGLDrawable() instanceof GLFBODrawable)) + return false; + + FBObject fboBack = ((GLFBODrawable)jdraw.getGLDrawable()).getFBObject(GL.GL_BACK); + if (fboBack == null) + return false; + + return (width != fboBack.getWidth() || height != fboBack.getHeight()); +} + + + // The native method for setting the Viewport. + @Override + void setViewport(Context ctx, int x, int y, int width, int height) { + if (VERBOSE) System.err.println("JoglPipeline.setViewport()"); + GL gl = context(ctx).getGL(); + gl.glViewport(x, y, width, height); + } + + // used for display Lists + @Override + void newDisplayList(Context ctx, int displayListId) { + if (VERBOSE) System.err.println("JoglPipeline.newDisplayList()"); + if (displayListId <= 0) { + System.err.println("JAVA 3D ERROR : glNewList(" + displayListId + ") -- IGNORED"); + } + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glNewList(displayListId, GL2.GL_COMPILE); + } + + @Override + void endDisplayList(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.endDisplayList()"); + GL2 gl = context(ctx).getGL().getGL2(); + gl.glEndList(); + } + + int numInvalidLists = 0; + @Override + void callDisplayList(Context ctx, int id, boolean isNonUniformScale) { + if (VERBOSE) System.err.println("JoglPipeline.callDisplayList()"); + if (id <= 0) { + if (numInvalidLists < 3) { + ++numInvalidLists; + System.err.println("JAVA 3D ERROR : glCallList(" + id + ") -- IGNORED"); + } else if (numInvalidLists == 3) { + ++numInvalidLists; + System.err.println("JAVA 3D : further glCallList error messages discarded"); + } + return; + } + + GL2 gl = context(ctx).getGL().getGL2(); + // Set normalization if non-uniform scale + if (isNonUniformScale) { + gl.glEnable(GL2.GL_NORMALIZE); + } + + gl.glCallList(id); + + // Turn normalization back off + if (isNonUniformScale) { + gl.glDisable(GL2.GL_NORMALIZE); + } + } + + @Override + void freeDisplayList(Context ctx, int id) { + if (VERBOSE) System.err.println("JoglPipeline.freeDisplayList()"); + if (id <= 0) { + System.err.println("JAVA 3D ERROR : glDeleteLists(" + id + ",1) -- IGNORED"); + } + + GL2 gl = context(ctx).getGL().getGL2(); + gl.glDeleteLists(id, 1); + } + + @Override + void freeTexture(Context ctx, int id) { + if (VERBOSE) System.err.println("JoglPipeline.freeTexture()"); + + GL gl = context(ctx).getGL(); + + if (id > 0) { + int[] tmp = new int[1]; + tmp[0] = id; + gl.glDeleteTextures(1, tmp, 0); + } else { + System.err.println("tried to delete tex with texid <= 0"); + } + } + + @Override + int generateTexID(Context ctx) { + if (VERBOSE) System.err.println("JoglPipeline.generateTexID()"); + + GL gl = context(ctx).getGL(); + int[] tmp = new int[] { -1 }; + gl.glGenTextures(1, tmp, 0); + + if (tmp[0] < 1) + return -1; + + return tmp[0]; + } + + @Override + void texturemapping(Context ctx, + int px, int py, + int minX, int minY, int maxX, int maxY, + int texWidth, int texHeight, + int rasWidth, + int format, int objectId, + byte[] imageYdown, + int winWidth, int winHeight) { + if (VERBOSE) System.err.println("JoglPipeline.texturemapping()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int glType = GL.GL_RGBA; + + // Temporarily disable fragment and most 3D operations + gl.glPushAttrib(GL2.GL_ENABLE_BIT | GL2.GL_TEXTURE_BIT | GL.GL_DEPTH_BUFFER_BIT | GL2.GL_POLYGON_BIT); + disableAttribFor2D(gl); + + // Reset the polygon mode + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL); + + gl.glDepthMask(false); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); + gl.glBindTexture(GL.GL_TEXTURE_2D, objectId); + // set up texture parameter + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT); + + gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + + gl.glEnable(GL.GL_TEXTURE_2D); + + // loaded identity modelview and projection matrix + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + + gl.glOrtho(0.0, winWidth, 0.0, winHeight, 0.0, 0.0); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + + if (gl.isExtensionAvailable("GL_EXT_abgr")) { + glType = GL2.GL_ABGR_EXT; + } else { + switch (format) { + case ImageComponentRetained.TYPE_BYTE_RGBA: + glType = GL.GL_RGBA; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + glType = GL.GL_RGB; + break; + } + } + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, rasWidth); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, minX); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, minY); + gl.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, minX, minY, + maxX - minX, maxY - minY, + glType, GL.GL_UNSIGNED_BYTE, + ByteBuffer.wrap(imageYdown)); + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, 0); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, 0); + + float texMinU = (float) minX/ (float) texWidth; + float texMinV = (float) minY/ (float) texHeight; + float texMaxU = (float) maxX/ (float) texWidth; + float texMaxV = (float) maxY/ (float) texHeight; + float halfWidth = (float)winWidth/2.0f; + float halfHeight = (float)winHeight/2.0f; + + float mapMinX = (float) (((px + minX)- halfWidth)/halfWidth); + float mapMinY = (float) ((halfHeight - (py + maxY))/halfHeight); + float mapMaxX = (float) ((px + maxX - halfWidth)/halfWidth); + float mapMaxY = (float) ((halfHeight - (py + minY))/halfHeight); + + gl.glBegin(GL2.GL_QUADS); + + gl.glTexCoord2f(texMinU, texMaxV); gl.glVertex2f(mapMinX,mapMinY); + gl.glTexCoord2f(texMaxU, texMaxV); gl.glVertex2f(mapMaxX,mapMinY); + gl.glTexCoord2f(texMaxU, texMinV); gl.glVertex2f(mapMaxX,mapMaxY); + gl.glTexCoord2f(texMinU, texMinV); gl.glVertex2f(mapMinX,mapMaxY); + gl.glEnd(); + + // Java 3D always clears the Z-buffer + gl.glDepthMask(true); + gl.glClear(GL.GL_DEPTH_BUFFER_BIT); + gl.glPopAttrib(); + } + + @Override + boolean initTexturemapping(Context ctx, int texWidth, + int texHeight, int objectId) { + if (VERBOSE) System.err.println("JoglPipeline.initTexturemapping()"); + + GL2 gl = context(ctx).getGL().getGL2(); + + int glType = (gl.isExtensionAvailable("GL_EXT_abgr") ? GL2.GL_ABGR_EXT : GL.GL_RGBA); + + gl.glBindTexture(GL.GL_TEXTURE_2D, objectId); + + gl.glTexImage2D(GL2.GL_PROXY_TEXTURE_2D, 0, GL.GL_RGBA, texWidth, + texHeight, 0, glType, GL.GL_UNSIGNED_BYTE, null); + + int[] width = new int[1]; + gl.glGetTexLevelParameteriv(GL2.GL_PROXY_TEXTURE_2D, 0, + GL2.GL_TEXTURE_WIDTH, width, 0); + + if (width[0] <= 0) { + return false; + } + + // init texture size only without filling the pixels + gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, texWidth, + texHeight, 0, glType, GL.GL_UNSIGNED_BYTE, null); + + return true; + } + + + // 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. + @Override + void setRenderMode(Context ctx, int mode, boolean doubleBuffer) { + if (VERBOSE) System.err.println("JoglPipeline.setRenderMode()"); + + GL2 gl = context(ctx).getGL().getGL2(); + int drawBuf = 0; + if (doubleBuffer) { + drawBuf = GL.GL_BACK; + switch (mode) { + case Canvas3D.FIELD_LEFT: + drawBuf = GL2.GL_BACK_LEFT; + break; + case Canvas3D.FIELD_RIGHT: + drawBuf = GL2.GL_BACK_RIGHT; + break; + case Canvas3D.FIELD_ALL: + drawBuf = GL.GL_BACK; + break; + } + } else { + drawBuf = GL.GL_FRONT; + switch (mode) { + case Canvas3D.FIELD_LEFT: + drawBuf = GL2.GL_FRONT_LEFT; + break; + case Canvas3D.FIELD_RIGHT: + drawBuf = GL2.GL_FRONT_RIGHT; + break; + case Canvas3D.FIELD_ALL: + drawBuf = GL.GL_FRONT; + break; + } + } + + gl.glDrawBuffer(drawBuf); + } + + // Set glDepthMask. + @Override + void setDepthBufferWriteEnable(Context ctx, boolean mode) { + if (VERBOSE) System.err.println("JoglPipeline.setDepthBufferWriteEnable()"); + + GL gl = context(ctx).getGL(); + if (mode) { + gl.glDepthMask(true); + } else { + gl.glDepthMask(false); + } + } + + //---------------------------------------------------------------------- + // Helper private functions for Canvas3D + // + + private boolean getPropertiesFromCurrentContext(JoglContext ctx, GL2 gl) { + + // FIXME: this is a heavily abridged set of the stuff in Canvas3D.c; + // probably need to pull much more in + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_UNITS, tmp, 0); + ctx.setMaxTexCoordSets(tmp[0]); + if (VirtualUniverse.mc.transparentOffScreen) { + ctx.setAlphaClearValue(0.0f); + } else { + ctx.setAlphaClearValue(1.0f); + } + if (gl.isExtensionAvailable("GL_ARB_vertex_shader")) { + gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_COORDS_ARB, tmp, 0); + ctx.setMaxTexCoordSets(tmp[0]); + } + return true; + } + + private int[] extractVersionInfo(String versionString) { + StringTokenizer tok = new StringTokenizer(versionString, ". "); + int major = Integer.valueOf(tok.nextToken()).intValue(); + int minor = Integer.valueOf(tok.nextToken()).intValue(); + + // See if there's vendor-specific information which might + // imply a more recent OpenGL version + tok = new StringTokenizer(versionString, " "); + if (tok.hasMoreTokens()) { + tok.nextToken(); + if (tok.hasMoreTokens()) { + Pattern p = Pattern.compile("\\D*(\\d+)\\.(\\d+)\\.?(\\d*).*"); + Matcher m = p.matcher(tok.nextToken()); + if (m.matches()) { + int altMajor = Integer.valueOf(m.group(1)).intValue(); + int altMinor = Integer.valueOf(m.group(2)).intValue(); + // Avoid possibly confusing situations by requiring + // major version to match + if (altMajor == major && + altMinor > minor) { + minor = altMinor; + } + } + } + } + return new int[] { major, minor }; + } + +// FIXME: GL_SGI_texture_color_table +// private int getTextureColorTableSize(GL gl) { +// if (!gl.isExtensionAvailable("GL_ARB_imaging")) { +// return 0; +// } +// +// gl.glColorTable(GL.GL_PROXY_TEXTURE_COLOR_TABLE_SGI, GL.GL_RGBA, 256, GL.GL_RGB, +// GL2.GL_INT, null); +// int[] tmp = new int[1]; +// gl.glGetColorTableParameteriv(GL.GL_PROXY_TEXTURE_COLOR_TABLE_SGI, +// GL2.GL_COLOR_TABLE_WIDTH, tmp, 0); +// return tmp[0]; +// } + + + private void checkTextureExtensions(Canvas3D cv, + JoglContext ctx, + GL gl, + boolean gl13) { + if (gl13) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_MULTI_TEXTURE; + cv.multiTexAccelerated = true; + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_UNITS, tmp, 0); + cv.maxTextureUnits = tmp[0]; + cv.maxTexCoordSets = cv.maxTextureUnits; + if (gl.isExtensionAvailable("GL_ARB_vertex_shader")) { + gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_COORDS_ARB, tmp, 0); + cv.maxTexCoordSets = tmp[0]; + } + } +// FIXME: GL_SGI_texture_color_table +// if (gl.isExtensionAvailable("GL_SGI_texture_color_table") || +// gl.isExtensionAvailable("GL_ARB_imaging")) { +// cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COLOR_TABLE; +// +// // get texture color table size +// // need to check later +// cv.textureColorTableSize = getTextureColorTableSize(gl); +// if (cv.textureColorTableSize <= 0) { +// cv.textureExtendedFeatures &= ~Canvas3D.TEXTURE_COLOR_TABLE; +// } +// if (cv.textureColorTableSize > 256) { +// cv.textureColorTableSize = 256; +// } +// } + + if (gl.isExtensionAvailable("GL_ARB_texture_env_combine")) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE; + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE_SUBTRACT; + } else if (gl.isExtensionAvailable("GL_EXT_texture_env_combine")) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE; + } + +// FIXME: GL_NV_register_combiners +// if (gl.isExtensionAvailable("GL_NV_register_combiners")) { +// cv.textureExtendedFeatures |= Canvas3D.TEXTURE_REGISTER_COMBINERS; +// } + + if (gl.isExtensionAvailable("GL_ARB_texture_env_dot3") || + gl.isExtensionAvailable("GL_EXT_texture_env_dot3")) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE_DOT3; + } + + if (gl13) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_CUBE_MAP; + } + +// FIXME: GL_SGIS_sharpen_texture +// if (gl.isExtensionAvailable("GL_SGIS_sharpen_texture")) { +// cv.textureExtendedFeatures |= Canvas3D.TEXTURE_SHARPEN; +// } + +// FIXME: GL_SGIS_sharpen_texture +// if (gl.isExtensionAvailable("GL_SGIS_detail_texture")) { +// cv.textureExtendedFeatures |= Canvas3D.TEXTURE_DETAIL; +// } + +// FIXME: GL_SGIS_texture_filter4 +// if (gl.isExtensionAvailable("GL_SGIS_texture_filter4")) { +// cv.textureExtendedFeatures |= Canvas3D.TEXTURE_FILTER4; +// } + + if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_ANISOTROPIC_FILTER; + float[] tmp = new float[1]; + gl.glGetFloatv(GL. GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, tmp, 0); + cv.anisotropicDegreeMax = tmp[0]; + } + +// FIXME: GL_SGIX_texture_lod_bias +// if (gl.isExtensionAvailable("GL_SGIX_texture_lod_bias")) { +// cv.textureExtendedFeatures |= Canvas3D.TEXTURE_LOD_OFFSET; +// } + + if (!VirtualUniverse.mc.enforcePowerOfTwo && + gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two")) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_NON_POWER_OF_TWO; + } + + if (gl.isExtensionAvailable("GL_SGIS_generate_mipmap")) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION; + } + + } + + + private void checkGLSLShaderExtensions(Canvas3D cv, + JoglContext ctx, + GL gl, + boolean hasgl13) { + + // Force shaders to be disabled, since no multitexture support + if (!hasgl13) + return; + + if (gl.isExtensionAvailable("GL_ARB_shader_objects") && + gl.isExtensionAvailable("GL_ARB_shading_language_100")) { + + // FIXME: this isn't complete and would need to set up the + // JoglContext for dispatch of various routines such as those + // related to vertex attributes + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_IMAGE_UNITS_ARB, tmp, 0); + cv.maxTextureImageUnits = tmp[0]; + gl.glGetIntegerv(GL2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, tmp, 0); + cv.maxVertexTextureImageUnits = tmp[0]; + gl.glGetIntegerv(GL2.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, tmp, 0); + cv.maxCombinedTextureImageUnits = tmp[0]; + int vertexAttrOffset = VirtualUniverse.mc.glslVertexAttrOffset; + ctx.setGLSLVertexAttrOffset(vertexAttrOffset); + gl.glGetIntegerv(GL2.GL_MAX_VERTEX_ATTRIBS_ARB, tmp, 0); + cv.maxVertexAttrs = tmp[0]; + // decr count to allow for reserved vertex attrs + cv.maxVertexAttrs -= vertexAttrOffset; + if (cv.maxVertexAttrs < 0) { + cv.maxVertexAttrs = 0; + } + cv.shadingLanguageGLSL = true; + } + } + + private void setupCanvasProperties(Canvas3D cv, JoglContext ctx, GL gl) { + // Note: this includes relevant portions from both the + // NativePipeline's getPropertiesFromCurrentContext and setupCanvasProperties + + // Reset all fields + cv.multiTexAccelerated = false; + cv.maxTextureUnits = 1; + cv.maxTexCoordSets = 1; + cv.maxTextureImageUnits = 0; + cv.maxVertexTextureImageUnits = 0; + cv.maxCombinedTextureImageUnits = 0; + cv.maxVertexAttrs = 0; + cv.extensionsSupported = 0; + cv.textureExtendedFeatures = 0; + cv.textureColorTableSize = 0; + cv.anisotropicDegreeMax = 0; + cv.textureBoundaryWidthMax = 0; + cv.textureWidthMax = 0; + cv.textureHeightMax = 0; + cv.texture3DWidthMax = 0; + cv.texture3DHeightMax = 0; + cv.texture3DDepthMax = 0; + cv.shadingLanguageGLSL = false; + + // Now make queries and set up these fields + String glVersion = gl.glGetString(GL.GL_VERSION); + String glVendor = gl.glGetString(GL.GL_VENDOR); + String glRenderer = gl.glGetString(GL.GL_RENDERER); + cv.nativeGraphicsVersion = glVersion; + cv.nativeGraphicsVendor = glVendor; + cv.nativeGraphicsRenderer = glRenderer; + + // find out the version, major and minor version number + int[] versionNumbers = extractVersionInfo(glVersion); + int major = versionNumbers[0]; + int minor = versionNumbers[1]; + + /////////////////////////////////////////// + // setup the graphics context properties // + + // NOTE: Java 3D now requires OpenGL 1.3 for full functionality. + // For backwards compatibility with certain older graphics cards and + // drivers (e.g., the Linux DRI driver for older ATI cards), + // we will try to run on OpenGL 1.2 in an unsupported manner. However, + // we will not attempt to use OpenGL extensions for any features that + // are available in OpenGL 1.3, specifically multitexture, multisample, + // and cube map textures. + + if (major < 1 || (major == 1 && minor < 2)) { + throw new IllegalRenderingStateException( + "Java 3D ERROR : OpenGL 1.2 or better is required (GL_VERSION=" + + major + "." + minor + ")"); + } + + boolean gl20 = false; + boolean gl14 = false; + boolean gl13 = false; + + if (major == 1) { + if (minor == 2) { + System.err.println("JAVA 3D: OpenGL 1.2 detected; will run with reduced functionality"); + } + if (minor >= 3) { + gl13 = true; + } + if (minor >= 4) { + gl14 = true; + } + } else /* major >= 2 */ { + gl13 = true; + gl14 = true; + gl20 = true; + } + + if (gl20) { + assert gl13; + assert gl14; + assert gl.isExtensionAvailable("GL_VERSION_2_0"); + } + + if (gl14) { + assert gl13; + assert gl.isExtensionAvailable("GL_VERSION_1_4"); + } + + if (gl13) { + assert gl.isExtensionAvailable("GL_VERSION_1_3"); + } + + // Set up properties for OpenGL 1.3 + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_3D; + + // Note that we don't query for GL_ARB_imaging here + + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_LOD_RANGE; + + if (gl14) { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION; + } + + // look for OpenGL 2.0 features + // Fix to Issue 455 : Need to disable NPOT textures for older cards that claim to support it. + // Some older cards (e.g., Nvidia fx500 and ATI 9800) claim to support OpenGL 2.0. + // This means that these cards have to support non-power-of-two (NPOT) texture, + // but their lack the necessary HW force the vendors the emulate this feature in software. + // The result is a ~100x slower down compare to power-of-two textures. + // Do not check for gl20 but instead check of GL_ARB_texture_non_power_of_two extension string + // if (gl20) { + // if(!VirtualUniverse.mc.enforcePowerOfTwo) { + // cv.textureExtendedFeatures |= Canvas3D.TEXTURE_NON_POWER_OF_TWO; + // } + // } + + + // Setup GL_EXT_abgr + if (gl.isExtensionAvailable("GL_EXT_abgr")) { + cv.extensionsSupported |= Canvas3D.EXT_ABGR; + } + + // GL_BGR is always supported + cv.extensionsSupported |= Canvas3D.EXT_BGR; + + // Setup multisample + // FIXME: this is not correct for the Windows platform yet + if (gl13) { + cv.extensionsSupported |= Canvas3D.MULTISAMPLE; + ctx.setHasMultisample(true); + } + + if ((cv.extensionsSupported & Canvas3D.MULTISAMPLE) != 0 && + !VirtualUniverse.mc.implicitAntialiasing) { + gl.glDisable(GL.GL_MULTISAMPLE); + } + + // Check texture extensions + checkTextureExtensions(cv, ctx, gl, gl13); + + // Check shader extensions + checkGLSLShaderExtensions(cv, ctx, gl, gl13); + + cv.textureBoundaryWidthMax = 1; + { + int[] tmp = new int[1]; + gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, tmp, 0); + cv.textureWidthMax = tmp[0]; + cv.textureHeightMax = tmp[0]; + + tmp[0] = -1; + gl.glGetIntegerv(GL2.GL_MAX_3D_TEXTURE_SIZE, tmp, 0); + cv.texture3DWidthMax = tmp[0]; + cv.texture3DHeightMax = tmp[0]; + cv.texture3DDepthMax = tmp[0]; + } + } + + /* + * Function to disable most rendering attributes when doing a 2D + * clear, image copy, or image composite operation. Note that the + * caller must save/restore the attributes with + * pushAttrib(GL_ENABLE_BIT|...) and popAttrib() + */ + private void disableAttribFor2D(GL gl) { + gl.glDisable(GL2.GL_ALPHA_TEST); + gl.glDisable(GL.GL_BLEND); + gl.glDisable(GL.GL_COLOR_LOGIC_OP); + gl.glDisable(GL2.GL_COLOR_MATERIAL); + gl.glDisable(GL.GL_CULL_FACE); + gl.glDisable(GL.GL_DEPTH_TEST); + gl.glDisable(GL2.GL_FOG); + gl.glDisable(GL2.GL_LIGHTING); + gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); + gl.glDisable(GL2.GL_POLYGON_STIPPLE); + gl.glDisable(GL.GL_STENCIL_TEST); + gl.glDisable(GL.GL_TEXTURE_2D); + gl.glDisable(GL2.GL_TEXTURE_GEN_Q); + gl.glDisable(GL2.GL_TEXTURE_GEN_R); + gl.glDisable(GL2.GL_TEXTURE_GEN_S); + gl.glDisable(GL2.GL_TEXTURE_GEN_T); + + + for (int i = 0; i < 6; i++) { + gl.glDisable(GL2.GL_CLIP_PLANE0 + i); + } + + gl.glDisable(GL2.GL_TEXTURE_3D); + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + +// FIXME: GL_NV_register_combiners +// if (gl.isExtensionAvailable("GL_NV_register_combiners")) { +// gl.glDisable(GL.GL_REGISTER_COMBINERS_NV); +// } +// FIXME: GL_SGI_texture_color_table +// if (gl.isExtensionAvailable("GL_SGI_texture_color_table")) { +// gl.glDisable(GL.GL_TEXTURE_COLOR_TABLE_SGI); +// } + } + + private void disableAttribForRaster(GL gl) { + + gl.glDisable(GL2.GL_COLOR_MATERIAL); + gl.glDisable(GL.GL_CULL_FACE); + gl.glDisable(GL2.GL_LIGHTING); + gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); + gl.glDisable(GL2.GL_POLYGON_STIPPLE); + + // TODO: Disable if Raster.CLIP_POSITION is true +// for (int i = 0; i < 6; i++) { +// gl.glDisable(GL2.GL_CLIP_PLANE0 + i); +// } + + } + + private void copyTranspose(double[] src, double[] dst) { + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = src[12]; + dst[4] = src[1]; + dst[5] = src[5]; + dst[6] = src[9]; + dst[7] = src[13]; + dst[8] = src[2]; + dst[9] = src[6]; + dst[10] = src[10]; + dst[11] = src[14]; + dst[12] = src[3]; + dst[13] = src[7]; + dst[14] = src[11]; + dst[15] = src[15]; + } + + // --------------------------------------------------------------------- + + // + // Canvas3D / GraphicsConfigTemplate3D methods - logic dealing with + // native graphics configuration or drawing surface + // + + // Return a graphics config based on the one passed in. Note that we can + // assert that the input config is non-null and was created from a + // GraphicsConfigTemplate3D. + // This method must return a valid GraphicsConfig, or else it must throw + // an exception if one cannot be returned. + @Override + GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig) { + if (VERBOSE) System.err.println("JoglPipeline.getGraphicsConfig()"); + + GraphicsConfigInfo gcInf0 = Canvas3D.graphicsConfigTable.get(gconfig); + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration)gcInf0.getPrivateData(); + + return awtConfig.getAWTGraphicsConfiguration(); + } + + private enum DisabledCaps { + STEREO, + AA, + DOUBLE_BUFFER, + } + + // Get best graphics config from pipeline + @Override + GraphicsConfiguration getBestConfiguration(GraphicsConfigTemplate3D gct, + GraphicsConfiguration[] gc) { + if (VERBOSE) System.err.println("JoglPipeline.getBestConfiguration()"); + + // Create a GLCapabilities based on the GraphicsConfigTemplate3D + final GLCapabilities caps = new GLCapabilities(profile); + + caps.setDoubleBuffered(gct.getDoubleBuffer() != GraphicsConfigTemplate.UNNECESSARY); + + caps.setStereo(gct.getStereo() != GraphicsConfigTemplate.UNNECESSARY); + + // Scene antialiasing only if double buffering + if (gct.getSceneAntialiasing() != GraphicsConfigTemplate.UNNECESSARY && + gct.getDoubleBuffer() != GraphicsConfigTemplate.UNNECESSARY) { + caps.setSampleBuffers(true); + caps.setNumSamples(2); + } else { + caps.setSampleBuffers(false); + caps.setNumSamples(0); + } + + caps.setDepthBits(gct.getDepthSize()); + caps.setStencilBits(gct.getStencilSize()); + + caps.setRedBits(Math.max(5, gct.getRedSize())); + caps.setGreenBits(Math.max(5, gct.getGreenSize())); + caps.setBlueBits(Math.max(5, gct.getBlueSize())); + + + // Issue 399: Request alpha buffer if transparentOffScreen is set + if (VirtualUniverse.mc.transparentOffScreen) { + caps.setAlphaBits(1); + } + + + // Add PREFERRED capabilities in order of least to highest priority and we will try disabling them + ArrayList capsToDisable = new ArrayList(); + + if (gct.getStereo() == GraphicsConfigTemplate.PREFERRED) { + capsToDisable.add(DisabledCaps.STEREO); + } + + if (gct.getSceneAntialiasing() == GraphicsConfigTemplate.PREFERRED) { + capsToDisable.add(DisabledCaps.AA); + } + + // if AA is required, so is double buffering. + if (gct.getSceneAntialiasing() != GraphicsConfigTemplate.REQUIRED && + gct.getDoubleBuffer() == GraphicsConfigTemplate.PREFERRED) { + capsToDisable.add(DisabledCaps.DOUBLE_BUFFER); + } + + + // Pick an arbitrary graphics device. + GraphicsDevice device = gc[0].getDevice(); + AbstractGraphicsScreen screen = (device != null) ? AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT) : + AWTGraphicsScreen.createDefault(); + + // Create a Frame and dummy GLCanvas to perform eager pixel format selection + + // Note that we loop in similar fashion to the NativePipeline's + // native code in the situation where we need to disable certain + // capabilities which aren't required + boolean tryAgain = true; + CapabilitiesCapturer capturer = null; + AWTGraphicsConfiguration awtConfig = null; + while (tryAgain) { + Frame f = new Frame(device.getDefaultConfiguration()); + f.setUndecorated(true); + f.setLayout(new BorderLayout()); + capturer = new CapabilitiesCapturer(); + try { + awtConfig = createAwtGraphicsConfiguration(caps, capturer, screen); + QueryCanvas canvas = new QueryCanvas(awtConfig, capturer); + f.add(canvas, BorderLayout.CENTER); + f.setSize(MIN_FRAME_SIZE, MIN_FRAME_SIZE); + f.setVisible(true); + canvas.doQuery(); + if (DEBUG_CONFIG) { + System.err.println("Waiting for CapabilitiesCapturer"); + } + // Try to wait for result without blocking EDT + if (!EventQueue.isDispatchThread()) { + synchronized(capturer) { + if (!capturer.done()) { + try { + capturer.wait(WAIT_TIME); + } catch (InterruptedException e) { + } + } + } + } + disposeOnEDT(f); + tryAgain = false; + } catch (GLException e) { + // Failure to select a pixel format; try switching off one + // of the only-preferred capabilities + if (capsToDisable.size() == 0) { + tryAgain = false; + } else { + switch (capsToDisable.remove(0)) { + case STEREO: + caps.setStereo(false); + break; + case AA: + caps.setSampleBuffers(false); + break; + case DOUBLE_BUFFER: + caps.setDoubleBuffered(false); + break; + } + awtConfig = null; + } + } + } + int chosenIndex = capturer.getChosenIndex(); + GLCapabilities chosenCaps = null; + if (chosenIndex < 0) { + if (DEBUG_CONFIG) { + System.err.println("CapabilitiesCapturer returned invalid index"); + } + // It's possible some platforms or implementations might not + // support the GLCapabilitiesChooser mechanism; feed in the + // same GLCapabilities later which we gave to the selector + chosenCaps = caps; + } else { + if (DEBUG_CONFIG) { + System.err.println("CapabilitiesCapturer returned index=" + chosenIndex); + } + chosenCaps = capturer.getCapabilities(); + } + + // FIXME chosenIndex isn't used anymore, used -1 instead of finding it. + JoglGraphicsConfiguration config = new JoglGraphicsConfiguration(chosenCaps, chosenIndex, device); + + + + // FIXME: because of the fact that JoglGraphicsConfiguration + // doesn't override hashCode() or equals(), we will basically be + // creating a new one each time getBestConfiguration() is + // called; in theory, we should probably map the same + // GLCapabilities on the same GraphicsDevice to the same + // JoglGraphicsConfiguration object + + // Cache the GraphicsTemplate3D + GraphicsConfigInfo gcInf0 = new GraphicsConfigInfo(gct); + gcInf0.setPrivateData(awtConfig); + + synchronized (Canvas3D.graphicsConfigTable) { + Canvas3D.graphicsConfigTable.put(config, gcInf0); + } + + return config; + } + + // Determine whether specified graphics config is supported by pipeline + @Override + boolean isGraphicsConfigSupported(GraphicsConfigTemplate3D gct, + GraphicsConfiguration gc) { + if (VERBOSE) System.err.println("JoglPipeline.isGraphicsConfigSupported()"); + + // FIXME: it looks like this method is implemented incorrectly + // in the existing NativePipeline in both the Windows and X11 + // ports. According to the semantics of the javadoc, it looks + // like this method is supposed to figure out the OpenGL + // capabilities which would be requested by the passed + // GraphicsConfiguration object were it to be used, and see + // whether it is possible to create a context with them. + // Instead, on both platforms, the implementations basically set + // up a query based on the contents of the + // GraphicsConfigTemplate3D object, using the + // GraphicsConfiguration object only to figure out on which + // GraphicsDevice and screen we're making the request, and see + // whether it's possible to choose an OpenGL pixel format based + // on that information. This makes this method less useful and + // we can probably just safely return true here uniformly + // without breaking anything. + return true; + } + + // Methods to get actual capabilities from Canvas3D + @Override + boolean hasDoubleBuffer(Canvas3D cv) { + if (VERBOSE) System.err.println("JoglPipeline.hasDoubleBuffer()"); + if (VERBOSE) System.err.println(" Returning " + caps(cv).getDoubleBuffered()); + return caps(cv).getDoubleBuffered(); + } + + @Override + boolean hasStereo(Canvas3D cv) { + if (VERBOSE) System.err.println("JoglPipeline.hasStereo()"); + if (VERBOSE) System.err.println(" Returning " + caps(cv).getStereo()); + return caps(cv).getStereo(); + } + + @Override + int getStencilSize(Canvas3D cv) { + if (VERBOSE) System.err.println("JoglPipeline.getStencilSize()"); + if (VERBOSE) System.err.println(" Returning " + caps(cv).getStencilBits()); + return caps(cv).getStencilBits(); + } + + @Override + boolean hasSceneAntialiasingMultisample(Canvas3D cv) { + if (VERBOSE) System.err.println("JoglPipeline.hasSceneAntialiasingMultisample()"); + if (VERBOSE) System.err.println(" Returning " + caps(cv).getSampleBuffers()); + + return caps(cv).getSampleBuffers(); + } + + @Override + boolean hasSceneAntialiasingAccum(Canvas3D cv) { + if (VERBOSE) System.err.println("JoglPipeline.hasSceneAntialiasingAccum()"); + GLCapabilities caps = caps(cv); + if (VERBOSE) System.err.println(" Returning " + (caps.getAccumRedBits() > 0 && + caps.getAccumGreenBits() > 0 && + caps.getAccumBlueBits() > 0)); + return (caps.getAccumRedBits() > 0 && + caps.getAccumGreenBits() > 0 && + caps.getAccumBlueBits() > 0); + } + + private boolean checkedForGetScreenMethod = false; + private Method getScreenMethod = null; + @Override + int getScreen(final GraphicsDevice graphicsDevice) { + if (VERBOSE) System.err.println("JoglPipeline.getScreen()"); + + if (!checkedForGetScreenMethod) { + // All of the Sun GraphicsDevice implementations have a method + // int getScreen(); + // which we want to call reflectively if it's available. + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + getScreenMethod = graphicsDevice.getClass().getDeclaredMethod("getScreen", new Class[] {}); + getScreenMethod.setAccessible(true); + } catch (Exception e) { + } + checkedForGetScreenMethod = true; + return null; + } + }); + } + + if (getScreenMethod != null) { + try { + return ((Integer) getScreenMethod.invoke(graphicsDevice, (Object[]) null)).intValue(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + return 0; + } + + //---------------------------------------------------------------------- + // Helper classes and methods to support query context functionality + // and pixel format selection + + interface ExtendedCapabilitiesChooser extends GLCapabilitiesChooser { + public void init(GLContext context); + } + + // Canvas subclass to help with various query operations such as the + // "query context" mechanism and pixel format selection. + // Must defeat and simplify the single-threading behavior of JOGL's + // GLCanvas in order to be able to set up a temporary pixel format + // and OpenGL context. Apparently simply turning off the + // single-threaded mode isn't enough to do this. + private final class QueryCanvas extends Canvas { + + private GLDrawable glDrawable; + private ExtendedCapabilitiesChooser chooser; + private boolean alreadyRan; + + private AWTGraphicsConfiguration awtConfig = null; + private JAWTWindow nativeWindow = null; + + private QueryCanvas(AWTGraphicsConfiguration awtConfig, + ExtendedCapabilitiesChooser chooser) { + // The platform-specific GLDrawableFactory will only provide a + // non-null GraphicsConfiguration on platforms where this is + // necessary (currently only X11, as Windows allows the pixel + // format of the window to be set later and Mac OS X seems to + // handle this very differently than all other platforms). On + // other platforms this method returns null; it is the case (at + // least in the Sun AWT implementation) that this will result in + // equivalent behavior to calling the no-arg super() constructor + // for Canvas. + super(awtConfig.getAWTGraphicsConfiguration()); + + this.awtConfig = awtConfig; + this.chooser = chooser; + } + + @Override + public void addNotify() { + super.addNotify(); + + nativeWindow = (JAWTWindow)NativeWindowFactory.getNativeWindow(this, awtConfig); + nativeWindow.lockSurface(); + try { + glDrawable = GLDrawableFactory.getFactory(profile).createGLDrawable(nativeWindow); + } + finally { + nativeWindow.unlockSurface(); + } + + glDrawable.setRealized(true); + } + + // It seems that at least on Mac OS X we need to do the OpenGL + // context-related work outside of the addNotify call because the + // Canvas hasn't been resized to a non-zero size by that point + private void doQuery() { + if (alreadyRan) + return; + GLContext context = glDrawable.createContext(null); + int res = context.makeCurrent(); + if (res != GLContext.CONTEXT_NOT_CURRENT) { + try { + chooser.init(context); + } finally { + context.release(); + } + } + context.destroy(); + alreadyRan = true; + + glDrawable.setRealized(false); + nativeWindow.destroy(); + } + } + + private static AWTGraphicsConfiguration createAwtGraphicsConfiguration(GLCapabilities capabilities, + CapabilitiesChooser chooser, + AbstractGraphicsScreen screen) { + GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilities.class); + AWTGraphicsConfiguration awtGraphicsConfiguration = (AWTGraphicsConfiguration) factory.chooseGraphicsConfiguration(capabilities, capabilities, + chooser, screen, VisualIDHolder.VID_UNDEFINED); + return awtGraphicsConfiguration; + } + + // Used in conjunction with IndexCapabilitiesChooser in pixel format + // selection -- see getBestConfiguration + static class CapabilitiesCapturer extends DefaultGLCapabilitiesChooser implements ExtendedCapabilitiesChooser { + private boolean done; + private GLCapabilities capabilities; + private int chosenIndex = -1; + + public boolean done() { + return done; + } + + public GLCapabilities getCapabilities() { + return capabilities; + } + + public int getChosenIndex() { + return chosenIndex; + } + + public int chooseCapabilities(GLCapabilities desired, + GLCapabilities[] available, + int windowSystemRecommendedChoice) { + int res = super.chooseCapabilities(desired, Arrays.asList(available), windowSystemRecommendedChoice); + capabilities = available[res]; + chosenIndex = res; + markDone(); + return res; + } + + @Override + public void init(GLContext context) { + // Avoid hanging things up for several seconds + kick(); + } + + private void markDone() { + synchronized (this) { + done = true; + notifyAll(); + } + } + + private void kick() { + synchronized (this) { + notifyAll(); + } + } + } + + // Used to support the query context mechanism -- needs to be more + // than just a GLCapabilitiesChooser + private final class ContextQuerier extends DefaultGLCapabilitiesChooser + implements ExtendedCapabilitiesChooser { + private Canvas3D canvas; + private boolean done; + + public ContextQuerier(Canvas3D canvas) { + this.canvas = canvas; + } + + public boolean done() { + return done; + } + + @Override + public void init(GLContext context) { + // This is basically a temporary + JoglContext jctx = new JoglContext(context); + GL2 gl = context.getGL().getGL2(); + // Set up various properties + if (getPropertiesFromCurrentContext(jctx, gl)) { + setupCanvasProperties(canvas, jctx, gl); + } + markDone(); + } + + private void markDone() { + synchronized (this) { + done = true; + notifyAll(); + } + } + } + + private void disposeOnEDT(final Frame f) { + Runnable r = new Runnable() { + @Override + public void run() { + f.setVisible(false); + f.dispose(); + } + }; + if (!EventQueue.isDispatchThread()) { + EventQueue.invokeLater(r); + } else { + r.run(); + } + } + + + // --------------------------------------------------------------------- + + // + // DrawingSurfaceObject methods + // + + // Method to construct a new DrawingSurfaceObject + @Override + DrawingSurfaceObject createDrawingSurfaceObject(Canvas3D cv) { + if (VERBOSE) System.err.println("JoglPipeline.createDrawingSurfaceObject()"); + return new JoglDrawingSurfaceObject(cv); + } + + // Method to free the drawing surface object + @Override + void freeDrawingSurface(Canvas3D cv, DrawingSurfaceObject drawingSurfaceObject) { + if (VERBOSE) System.err.println("JoglPipeline.freeDrawingSurface()"); + // This method is a no-op + } + + // Method to free the native drawing surface object + @Override + void freeDrawingSurfaceNative(Object o) { + if (VERBOSE) System.err.println("JoglPipeline.freeDrawingSurfaceNative()"); + // This method is a no-op + } + + //---------------------------------------------------------------------- + // Context-related routines + // + + // Helper used everywhere + GLContext context(Context ctx) { + if (ctx == null) + return null; + return ((JoglContext) ctx).getGLContext(); + } + + // Helper used everywhere + GLDrawable drawable(Drawable drawable) { + if (drawable == null) + return null; + return ((JoglDrawable) drawable).getGLDrawable(); + } + + GLCapabilities caps(Canvas3D ctx) { + if (ctx.drawable != null) { + // latest state for on- and offscreen drawables + return (GLCapabilities)drawable(ctx.drawable).getChosenGLCapabilities(); + } + else { + // state at the time of 'getBestConfiguration' + return ((JoglGraphicsConfiguration) ctx.graphicsConfiguration).getGLCapabilities(); + } + } + + //---------------------------------------------------------------------- + // General helper routines + // + + private static ThreadLocal nioVertexTemp = new ThreadLocal(); + private static ThreadLocal nioVertexDoubleTemp = new ThreadLocal(); + private static ThreadLocal nioColorTemp = new ThreadLocal(); + private static ThreadLocal nioColorByteTemp = new ThreadLocal(); + private static ThreadLocal nioNormalTemp = new ThreadLocal(); + private static ThreadLocal nioTexCoordSetTemp = new ThreadLocal(); + private static ThreadLocal nioVertexAttrSetTemp = new ThreadLocal(); + + private static FloatBuffer getVertexArrayBuffer(float[] vertexArray) { + return getVertexArrayBuffer(vertexArray, true); + } + + private static FloatBuffer getVertexArrayBuffer(float[] vertexArray, boolean copyData) { + return getNIOBuffer(vertexArray, nioVertexTemp, copyData); + } + + private static DoubleBuffer getVertexArrayBuffer(double[] vertexArray) { + return getVertexArrayBuffer(vertexArray, true); + } + + private static DoubleBuffer getVertexArrayBuffer(double[] vertexArray, boolean copyData) { + return getNIOBuffer(vertexArray, nioVertexDoubleTemp, true); + } + + private static FloatBuffer getColorArrayBuffer(float[] colorArray) { + return getColorArrayBuffer(colorArray, true); + } + + private static FloatBuffer getColorArrayBuffer(float[] colorArray, boolean copyData) { + return getNIOBuffer(colorArray, nioColorTemp, true); + } + + private static ByteBuffer getColorArrayBuffer(byte[] colorArray) { + return getColorArrayBuffer(colorArray, true); + } + + private static ByteBuffer getColorArrayBuffer(byte[] colorArray, boolean copyData) { + return getNIOBuffer(colorArray, nioColorByteTemp, true); + } + + private static FloatBuffer getNormalArrayBuffer(float[] normalArray) { + return getNormalArrayBuffer(normalArray, true); + } + + private static FloatBuffer getNormalArrayBuffer(float[] normalArray, boolean copyData) { + return getNIOBuffer(normalArray, nioNormalTemp, true); + } + + private static FloatBuffer[] getTexCoordSetBuffer(Object[] texCoordSet) { + return getNIOBuffer(texCoordSet, nioTexCoordSetTemp); + } + + private static FloatBuffer[] getVertexAttrSetBuffer(Object[] vertexAttrSet) { + return getNIOBuffer(vertexAttrSet, nioVertexAttrSetTemp); + } + + private static FloatBuffer getNIOBuffer(float[] array, ThreadLocal threadLocal, boolean copyData) { + if (array == null) { + return null; + } + FloatBuffer buf = threadLocal.get(); + if (buf == null) { + buf = Buffers.newDirectFloatBuffer(array.length); + threadLocal.set(buf); + } else { + buf.rewind(); + if (buf.remaining() < array.length) { + int newSize = Math.max(2 * buf.remaining(), array.length); + buf = Buffers.newDirectFloatBuffer(newSize); + threadLocal.set(buf); + } + } + if (copyData) { + buf.put(array); + buf.rewind(); + } + return buf; + } + + private static DoubleBuffer getNIOBuffer(double[] array, ThreadLocal threadLocal, boolean copyData) { + if (array == null) { + return null; + } + DoubleBuffer buf = threadLocal.get(); + if (buf == null) { + buf = Buffers.newDirectDoubleBuffer(array.length); + threadLocal.set(buf); + } else { + buf.rewind(); + if (buf.remaining() < array.length) { + int newSize = Math.max(2 * buf.remaining(), array.length); + buf = Buffers.newDirectDoubleBuffer(newSize); + threadLocal.set(buf); + } + } + if (copyData) { + buf.put(array); + buf.rewind(); + } + return buf; + } + + private static ByteBuffer getNIOBuffer(byte[] array, ThreadLocal threadLocal, boolean copyData) { + if (array == null) { + return null; + } + ByteBuffer buf = threadLocal.get(); + if (buf == null) { + buf = Buffers.newDirectByteBuffer(array.length); + threadLocal.set(buf); + } else { + buf.rewind(); + if (buf.remaining() < array.length) { + int newSize = Math.max(2 * buf.remaining(), array.length); + buf = Buffers.newDirectByteBuffer(newSize); + threadLocal.set(buf); + } + } + if (copyData) { + buf.put(array); + buf.rewind(); + } + return buf; + } + + private static FloatBuffer[] getNIOBuffer(Object[] array, ThreadLocal threadLocal) { + if (array == null) { + return null; + } + FloatBuffer[] bufs = threadLocal.get(); + + // First resize array of FloatBuffers + if (bufs == null) { + bufs = new FloatBuffer[array.length]; + threadLocal.set(bufs); + } else if (bufs.length < array.length) { + FloatBuffer[] newBufs = new FloatBuffer[array.length]; + System.arraycopy(bufs, 0, newBufs, 0, bufs.length); + bufs = newBufs; + threadLocal.set(bufs); + } + + // Now go down array of arrays, converting each into a direct FloatBuffer + for (int i = 0; i < array.length; i++) { + float[] cur = (float[]) array[i]; + FloatBuffer buf = bufs[i]; + if (buf == null) { + buf = Buffers.newDirectFloatBuffer(cur.length); + bufs[i] = buf; + } else { + buf.rewind(); + if (buf.remaining() < cur.length) { + int newSize = Math.max(2 * buf.remaining(), cur.length); + buf = Buffers.newDirectFloatBuffer(newSize); + bufs[i] = buf; + } + } + buf.put(cur); + buf.rewind(); + } + + return bufs; + } +} diff --git a/src/javax/media/j3d/JoglShaderObject.java b/src/javax/media/j3d/JoglShaderObject.java new file mode 100644 index 0000000..8f40834 --- /dev/null +++ b/src/javax/media/j3d/JoglShaderObject.java @@ -0,0 +1,39 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +class JoglShaderObject implements ShaderProgramId, ShaderId, ShaderAttrLoc { + private int val; + + JoglShaderObject(int val) { + this.val = val; + } + + int getValue() { + return val; + } +} diff --git a/src/javax/media/j3d/LOD.java b/src/javax/media/j3d/LOD.java new file mode 100644 index 0000000..22b7cca --- /dev/null +++ b/src/javax/media/j3d/LOD.java @@ -0,0 +1,236 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/Leaf.java b/src/javax/media/j3d/Leaf.java new file mode 100644 index 0000000..f0ae9b3 --- /dev/null +++ b/src/javax/media/j3d/Leaf.java @@ -0,0 +1,48 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * NOTE: Applications should not extend this class directly. + */ + +public abstract class Leaf extends Node { + + /** + * Construct and initialize the Leaf object. + */ + public Leaf(){ + } + +} diff --git a/src/javax/media/j3d/LeafRetained.java b/src/javax/media/j3d/LeafRetained.java new file mode 100644 index 0000000..720bf5b --- /dev/null +++ b/src/javax/media/j3d/LeafRetained.java @@ -0,0 +1,60 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +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/javax/media/j3d/Light.java b/src/javax/media/j3d/Light.java new file mode 100644 index 0000000..7e99843 --- /dev/null +++ b/src/javax/media/j3d/Light.java @@ -0,0 +1,729 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Enumeration; + +import javax.vecmath.Color3f; + +/** + * 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). + *

+ * 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. + *

+ * 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. + *

+ * Light Color + *

+ * 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. + *

+ * 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. + *

+ * 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 (R1, G1, B1) and + * the second light emits (R2, G2, + * B2), the components are added together giving + * (R1+R2, G1+G2, + * B1+B2). + * 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. + *

+ * Material Colors + *

+ * 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. + *

+ * 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. + *

+ * Influencing Bounds + *

+ * 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. + *

+ * 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). + *

+ * Limiting the Scope + *

+ * 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 list of scopes. + * Methods in the Light class permit the setting, addition, insertion, + * removal, and enumeration of nodes in the list of scopes. + *

+ * Two-sided Lighting of Polygons + *

+ * 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. + *

+ * For more information, see the "Face culling" and "Back-facing + * normal flip" descriptions in the PolygonAttributes class + * description. + *

+ * Turning on the Lights + *

+ * 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. + *

+ * 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. + *

+ * To disable lighting, call setEnable with false 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_STATE_READ, + ALLOW_COLOR_READ, + ALLOW_INFLUENCING_BOUNDS_READ, + ALLOW_SCOPE_READ + }; + + /** + * Constructs a Light node with default parameters. The default + * values are as follows: + *

    + * enable flag : true
    + * color : white (1,1,1)
    + * scope : empty (universe scope)
    + * influencing bounds : null
    + * influencing bounding leaf : null
    + *
+ */ + public Light() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs and initializes a Light node using the specified color. + * @param color the color of the light source + */ + public Light(Color3f color) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ((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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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(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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/LightBin.java b/src/javax/media/j3d/LightBin.java new file mode 100644 index 0000000..420d942 --- /dev/null +++ b/src/javax/media/j3d/LightBin.java @@ -0,0 +1,464 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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 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 0) { + e = 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 = 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>= 1; + i++; + } + + cv.canvasDirty &= ~Canvas3D.LIGHTBIN_DIRTY; + } + else if ((pointLts.size() > 0) && ((cv.canvasDirty & Canvas3D.VIEW_MATRIX_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++) { + PointLightRetained lt = pointLts.get(i); + lt.update(cv.ctx, pointLtsSlotIndex[i], scale); + cv.lights[pointLtsSlotIndex[i]] = lt; + cv.frameCount[pointLtsSlotIndex[i]] = frameCount; + } + } + } +} diff --git a/src/javax/media/j3d/LightRetained.java b/src/javax/media/j3d/LightRetained.java new file mode 100644 index 0000000..7780713 --- /dev/null +++ b/src/javax/media/j3d/LightRetained.java @@ -0,0 +1,1074 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Color3f; + +/** + * 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(); + Object[] scopeInfo = new Object[3]; + + GroupRetained group = 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); + 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 = scopes.remove(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 = 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(); + + for(int index = n-1; index >= 0; index--) { + GroupRetained group = 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)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((Group)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(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(Context 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) { + 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 = 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 = mLgts[i].boundingLeaf.transformedRegion; + } + else { // evaluate regionOfInfluence if not null + if (mLgts[i].regionOfInfluence != null) { + mLgts[i].region = 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 j, i; + 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; + 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 + @Override + void updateMirrorObject(Object[] objs) { + + 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 + */ + + @Override + 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; + } + } + } + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + if (!inSharedGroup) { + leafList.add(mirrorLights[0]); + } + else { + for (int i=0; i 0) { + J3dMessage createMessage = new J3dMessage(); + LightRetained[] mlts = new LightRetained[newlyAddedMirrorLights.size()]; + for (int i = 0; i < mlts.length; i++) { + mlts[i] = 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 = 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]; + 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 + */ + @Override + protected Object clone() { + LightRetained lr = (LightRetained)super.clone(); + lr.color = new Color3f(color); + lr.scopes = new Vector(scopes); + 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 + @Override + 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 = new J3dMessage(); + 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); + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + if (regionOfInfluence != null) { + regionOfInfluence.transform(xform.transform); + } + } +} + diff --git a/src/javax/media/j3d/LightSet.java b/src/javax/media/j3d/LightSet.java new file mode 100644 index 0000000..db4f065 --- /dev/null +++ b/src/javax/media/j3d/LightSet.java @@ -0,0 +1,102 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +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; inot a multiple of 2 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 2 + * or vertexCount is not a multiple of 2 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty LineArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 2 + * or vertexCount is not a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public LineArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + 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. + */ + @Override + void createRetained() { + this.retained = new LineArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + LineArrayRetained rt = (LineArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + LineArray l = new LineArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes); + l.duplicateNodeComponent(this); + return l; + } + +} diff --git a/src/javax/media/j3d/LineArrayRetained.java b/src/javax/media/j3d/LineArrayRetained.java new file mode 100644 index 0000000..a970d96 --- /dev/null +++ b/src/javax/media/j3d/LineArrayRetained.java @@ -0,0 +1,456 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + Point3d pnts[] = new Point3d[2]; + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + int[] vtxIndexArr = new int[2]; + + 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) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin, + pickRay.direction, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectLineAndRay(pnts[0], pnts[1], + pickSegment.start, + dir, sdist, iPnt) && + (sdist[0] <= 1.0)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + while (i < validVertexCount) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<2; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + + } + + @Override + 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 + *

  • Pattern - specifies the pattern used to draw the line:

    + *

      + *
    • PATTERN_SOLID - draws a solid line with no pattern. This is + * the default.
    • + *

      + *

    • PATTERN_DASH - draws dashed lines. Ideally, these will be drawn with + * a repeating pattern of 8 pixels on and 8 pixels off.
    • + *

      + *

    • PATTERN_DOT - draws dotted lines. Ideally, these will be drawn with + * a repeating pattern of 1 pixel on and 7 pixels off.
    • + *

      + *

    • 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.
    • + *

      + *

    • PATTERN_USER_DEFINED - draws lines with a user-defined line pattern. + * See "User-defined Line Patterns," below.
    • + *

    + *

    + *

  • Antialiasing (enabled or disabled). By default, antialiasing + * is disabled.
  • + *

    + *

    + * 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. + *

    + *
  • Width (in pixels). The default is a line width of one pixel. + *

+ * + * User-defined Line Patterns + *

+ * A user-defined line pattern is specified with a pattern mask and + * an optional scale factor. + *

+ * The Pattern Mask

+ * + * 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. + *

+ * 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. + *

+ * The Scale Factor + *

+ * 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.

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_ANTIALIASING_READ, + ALLOW_PATTERN_READ, + ALLOW_WIDTH_READ + }; + + /** + * Constructs a LineAttributes object with default parameters. + * The default values are as follows: + *

    + * line width : 1
    + * line pattern : PATTERN_SOLID
    + * pattern mask : 0xffff
    + * pattern scale factor : 1
    + * line antialiasing : false
    + *
+ */ + public LineAttributes(){ + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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")); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + *

+ * 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. + *

+ * 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. + *

+ * @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. + */ + @Override + void createRetained() { + this.retained = new LineAttributesRetained(); + this.retained.setSource(this); + } + + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + LineAttributes la = new LineAttributes(); + la.duplicateNodeComponent(this); + return la; + } + + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/LineAttributesRetained.java b/src/javax/media/j3d/LineAttributesRetained.java new file mode 100644 index 0000000..616a2ab --- /dev/null +++ b/src/javax/media/j3d/LineAttributesRetained.java @@ -0,0 +1,344 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + */ + @Override + 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 method updates the native context. + */ + void updateNative(Context ctx) { + Pipeline.getPipeline().updateLineAttributes(ctx, + lineWidth, linePattern, linePatternMask, + linePatternScaleFactor, lineAntialiasing); + } + + + /** + * Initializes a mirror object, point the mirror object to the retained + * object if the object is not editable + */ + @Override + synchronized void initMirrorObject() { + ((LineAttributesRetained)mirror).set(this); + } + + /** Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + + } + @Override + 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/javax/media/j3d/LineStripArray.java b/src/javax/media/j3d/LineStripArray.java new file mode 100644 index 0000000..f97574e --- /dev/null +++ b/src/javax/media/j3d/LineStripArray.java @@ -0,0 +1,217 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 2 + * or any element in the stripVertexCounts array is less than 2 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int[])} + * for more exceptions that can be thrown + */ + 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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 2 + * or any element in the stripVertexCounts array is less than 2 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty LineStripArray object using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 2 + * or any element in the stripVertexCounts array is less than 2 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public LineStripArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int[] stripVertexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + stripVertexCounts); + + if (vertexCount < 2 ) + throw new IllegalArgumentException(J3dI18N.getString("LineStripArray0")); + } + + /** + * Creates the retained mode LineStripArrayRetained object that this + * LineStripArray object will point to. + */ + @Override + void createRetained() { + this.retained = new LineStripArrayRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + LineStripArrayRetained rt = (LineStripArrayRetained) retained; + int stripcounts[] = new int[rt.getNumStrips()]; + rt.getStripVertexCounts(stripcounts); + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + LineStripArray l = new LineStripArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + stripcounts); + l.duplicateNodeComponent(this); + return l; + } +} diff --git a/src/javax/media/j3d/LineStripArrayRetained.java b/src/javax/media/j3d/LineStripArrayRetained.java new file mode 100644 index 0000000..861c306 --- /dev/null +++ b/src/javax/media/j3d/LineStripArrayRetained.java @@ -0,0 +1,556 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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(); + int[] vtxIndexArr = new int[2]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while(i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin, + pickRay.direction, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[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++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectLineAndRay(pnts[0], pnts[1], + pickSegment.start, + dir, sdist, iPnt) && + (sdist[0] <= 1.0)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + vtxIndexArr[0] = j; + getVertexData(j++, pnts[0]); + while (j < end) { + vtxIndexArr[1] = j; + getVertexData(j++, pnts[1]); + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + @Override + 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; + } + + @Override + 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 + @Override + 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 + @Override + 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; + } + } + + @Override + int getClassType() { + return LINE_TYPE; + } + +} diff --git a/src/javax/media/j3d/LinearFog.java b/src/javax/media/j3d/LinearFog.java new file mode 100644 index 0000000..a3f6b0a --- /dev/null +++ b/src/javax/media/j3d/LinearFog.java @@ -0,0 +1,272 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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. + *

+ * The linear fog blending factor, f, is computed as follows: + *

    + * f = (backDistance - z) / (backDistance - frontDistance) + *
+ * where: + *
    + * z is the distance from the viewpoint.
    + * frontDistance is the distance at which fog starts obscuring objects.
    + * backDistance is the distance at which fog totally obscurs objects. + *
+ */ +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_DISTANCE_READ + }; + + /** + * Constructs a LinearFog node with default parameters. + * The default values are as follows: + *
    + * front distance : 0.1
    + * back distance : 1.0
    + *
+ */ + public LinearFog() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs a LinearFog node with the specified fog color. + * @param color the fog color + */ + public LinearFog(Color3f color) { + super(color); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + void createRetained() { + this.retained = new LinearFogRetained(); + this.retained.setSource(this); + } + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + LinearFog lf = new LinearFog(); + lf.duplicateNode(this, forceDuplicate); + return lf; + } + + + /** + * Copies all LinearFog information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/LinearFogRetained.java b/src/javax/media/j3d/LinearFogRetained.java new file mode 100644 index 0000000..293c592 --- /dev/null +++ b/src/javax/media/j3d/LinearFogRetained.java @@ -0,0 +1,200 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3f; + +/** + * The LinearFog leaf node defines distance parameters for + * linear fog. + */ +class LinearFogRetained extends FogRetained { + /** + * Fog front and back distance + */ + private double frontDistance = 0.1; + private double backDistance = 1.0; + private double frontDistanceInEc; + private double backDistanceInEc; + + // 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 = new J3dMessage(); + 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 = new J3dMessage(); + 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. + */ + @Override + void update(Context ctx, double scale) { + validateDistancesInEc(scale); + Pipeline.getPipeline().updateLinearFog(ctx, + color.x, color.y, color.z, frontDistanceInEc, backDistanceInEc); + } + + + + @Override + void setLive(SetLiveState s) { + super.setLive(s); + + // Initialize the mirror object, this needs to be done, when + // renderBin is not accessing any of the fields + J3dMessage createMessage = new J3dMessage(); + 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++) { + GroupRetained group = 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 + @Override + synchronized void updateMirrorObject(Object[] objs) { + + int component = ((Integer)objs[1]).intValue(); + + 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).setLocalToVworldScale(getLastLocalToVworld().getDistanceScale()); + + super.updateMirrorObject(objs); + } + + /** + * Scale distances from local to eye coordinate + */ + @Override + protected void validateDistancesInEc(double vworldToCoexistenceScale) { + // vworldToCoexistenceScale can be used here since + // CoexistenceToEc has a unit scale + double localToEcScale = getLocalToVworldScale() * vworldToCoexistenceScale; + + frontDistanceInEc = frontDistance * localToEcScale; + backDistanceInEc = backDistance * localToEcScale; + } + +} diff --git a/src/javax/media/j3d/Link.java b/src/javax/media/j3d/Link.java new file mode 100644 index 0000000..e8c0237 --- /dev/null +++ b/src/javax/media/j3d/Link.java @@ -0,0 +1,167 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SHARED_GROUP_READ + }; + + /** + * Constructs a Link node object that does not yet point to a + * SharedGroup node. + */ + public Link() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs a Link node object that points to the specified + * SharedGroup node. + * @param sharedGroup the SharedGroup node + */ + public Link(SharedGroup sharedGroup) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((LinkRetained)this.retained).setSharedGroup(sharedGroup); + } + + /** + * Creates the retained mode LinkRetained object that this + * Link object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + *
+ * 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Link l = new Link(); + l.duplicateNode(this, forceDuplicate); + return l; + } + + /** + * Copies all Link information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + super.duplicateAttributes(originalNode, forceDuplicate); + ((LinkRetained) retained).setSharedGroup( + ((LinkRetained) originalNode.retained).getSharedGroup()); + } +} diff --git a/src/javax/media/j3d/LinkRetained.java b/src/javax/media/j3d/LinkRetained.java new file mode 100644 index 0000000..4ad5ddf --- /dev/null +++ b/src/javax/media/j3d/LinkRetained.java @@ -0,0 +1,353 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; +import java.util.ArrayList; + +/** + * 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((Bounds)null); + } + + /** + * 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.remove(this); + } + } + + if (sharedGroup != null) { + this.sharedGroup = + (SharedGroupRetained)sharedGroup.retained; + } else { + this.sharedGroup = null; + } + + if (source.isLive() && (sharedGroup != null)) { + + this.sharedGroup.parents.add(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); + } + + @Override + 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 + */ + @Override + Bounds getBounds() { + return (boundsAutoCompute ? + (Bounds)sharedGroup.getBounds().clone() : + super.getBounds()); + } + + + /** + * assign a name to this node when it is made live. + */ + @Override + 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.add(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(); + } + + @Override + 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); + } + } + } + + + @Override + void recombineAbove() { + localBounds.setWithLock(sharedGroup.localBounds); + parent.recombineAbove(); + } + + /** + * assign a name to this node when it is made live. + */ + @Override + 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.remove(this); + this.sharedGroup.clearLive(s); + s.inSharedGroup = inSharedGroup; + s.keys = oldKeys; + } else { + super.clearLive(s); + } + } + + @Override + 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) { + for(int i=0; i switchLinks = parentSwitchLink.childrenSwitchLinks.get(i); + if (switchLinks.contains(this)) { + switchLinks.remove(this); + break; + } + } + } + } + super.removeNodeData(s); + } + + + @Override + void updatePickable(HashKey keys[], boolean pick[]) { + super.updatePickable(keys, pick); + + if (sharedGroup != null) { + HashKey newKeys[] = getNewKeys(locale.nodeId, keys); + sharedGroup.updatePickable(newKeys, pick); + } + } + + @Override + void updateCollidable(HashKey keys[], boolean collide[]) { + super.updateCollidable(keys, collide); + + if (sharedGroup != null) { + HashKey newKeys[] = getNewKeys(locale.nodeId, keys); + sharedGroup.updateCollidable(newKeys, collide); + } + } + + @Override + void setBoundsAutoCompute(boolean autoCompute) { + super.setBoundsAutoCompute(autoCompute); + if (!autoCompute) { + localBounds = getBounds(); + } + } + + @Override + void setCompiled() { + super.setCompiled(); + if (sharedGroup != null) { + sharedGroup.setCompiled(); + } + } + + @Override + void compile(CompileState compState) { + + super.compile(compState); + + // XXXX: 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; + } + + @Override + void searchGeometryAtoms(UnorderList list) { + if (sharedGroup != null) { + sharedGroup.searchGeometryAtoms(list); + } + } +} diff --git a/src/javax/media/j3d/Locale.java b/src/javax/media/j3d/Locale.java new file mode 100644 index 0000000..4da6c4a --- /dev/null +++ b/src/javax/media/j3d/Locale.java @@ -0,0 +1,1088 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +/** + * 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. + *

+ * A Locale object defines methods to set and get its high-resolution + * coordinates, and methods to add, remove, and enumerate the branch + * graphs. + * + *

+ * For more information, see the + * Introduction to the Java 3D API and + * Scene Graph Superstructure + * documents. + * + * @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.notifyStructureChangeListeners(true, this, branchGroup); + 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 = new J3dMessage(); + 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 = new J3dMessage(); + 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 = new J3dMessage(); + 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 = s.behaviorNodes.get(i); + b.executeInitialize(); + } + + createMessage = new J3dMessage(); + 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 = new J3dMessage(); + } 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(); + + // Issue 312: We need to send the REMOVE_NODES message to the + // RenderingEnvironmentStructure before we send VIEWSPECIFICGROUP_CLEAR, + // since the latter clears the list of views that is referred to by + // scopedNodesViewList and used by removeNodes. + if (messages == null) { + VirtualUniverse.mc.processMessage(destroyMessage); + destroyMessage = new J3dMessage(); + } 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); + destroyMessage = new J3dMessage(); + } 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); + } else { + destroyMessage = messages[startIndex++]; + } + + if (universe.isEmpty()) { + VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE, + universe); + } + universe.setLiveState.reset(null); // cleanup memory + universe.notifyStructureChangeListeners(false, this, branchGroup); + } + + /** + * 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(); + universe.notifyStructureChangeListeners(true, this, newGroup); + synchronized (universe.sceneGraphLock) { + doReplaceBranchGraph(oldGroup, newGroup); + universe.setLiveState.reset(this); + } + universe.notifyStructureChangeListeners(false, this, oldGroup); + 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 = new J3dMessage(); + + 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 = new J3dMessage(); + 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 = new J3dMessage(); + 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 = new J3dMessage(); + 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); + + // XXXX: make these two into one message + createMessage = new J3dMessage(); + 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); + + BehaviorRetained[] behavNodes = new BehaviorRetained[universe.setLiveState.behaviorNodes.size()]; + behavNodes = universe.setLiveState.behaviorNodes.toArray(behavNodes); + + if (universe.isEmpty()) { + VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE, + universe); + } + + for (int i=0; i< behavNodes.length; i++) { + behavNodes[i].executeInitialize(); + } + + createMessage = new J3dMessage(); + 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(); + } + + + void validateModeFlagAndPickShape(int mode, int flags, PickShape pickShape) { + + if (universe == null) { + throw new IllegalStateException(J3dI18N.getString("Locale4")); + } + + if((mode != PickInfo.PICK_BOUNDS) && (mode != PickInfo.PICK_GEOMETRY)) { + + throw new IllegalArgumentException(J3dI18N.getString("Locale5")); + } + + if((pickShape instanceof PickPoint) && (mode == PickInfo.PICK_GEOMETRY)) { + throw new IllegalArgumentException(J3dI18N.getString("Locale6")); + } + + if(((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) && + ((flags & PickInfo.ALL_GEOM_INFO) != 0)) { + throw new IllegalArgumentException(J3dI18N.getString("Locale7")); + } + + if((mode == PickInfo.PICK_BOUNDS) && + (((flags & (PickInfo.CLOSEST_GEOM_INFO | + PickInfo.ALL_GEOM_INFO | + PickInfo.CLOSEST_DISTANCE | + PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) { + + throw new IllegalArgumentException(J3dI18N.getString("Locale8")); + } + + if((pickShape instanceof PickBounds) && + (((flags & (PickInfo.CLOSEST_GEOM_INFO | + PickInfo.ALL_GEOM_INFO | + PickInfo.CLOSEST_DISTANCE | + PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) { + + throw new IllegalArgumentException(J3dI18N.getString("Locale9")); + } + } + + /** + * Returns an array referencing all the items that are pickable below this + * Locale 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")); + } + + PickInfo[] pickInfoArr = pickAll( PickInfo.PICK_BOUNDS, + PickInfo.SCENEGRAPHPATH, pickShape); + + if(pickInfoArr == null) { + return null; + } + SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length]; + for( int i=0; iLocale that intersect with PickShape. + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *

    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if this Locale has been + * removed from its VirtualUniverse. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see BranchGroup#pickAll(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo[] pickAll( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + + GeometryAtom geomAtoms[] = universe.geometryStructure.pickAll(this, pickShape); + + return PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL); + + } + + /** + * Returns a sorted array of references to all the pickable items + * that intersect with the pickShape. Element [0] references the + * item closest to origin of PickShape successive array + * elements are further from the origin + *
+ * 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")); + } + + PickInfo[] pickInfoArr = pickAllSorted( PickInfo.PICK_BOUNDS, + PickInfo.SCENEGRAPHPATH, pickShape); + + if(pickInfoArr == null) { + return null; + } + SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length]; + for( int i=0; iorigin of PickShape successive array + * elements are further from the origin + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if this Locale has been + * removed from its VirtualUniverse. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see BranchGroup#pickAllSorted(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo[] pickAllSorted( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + GeometryAtom geomAtoms[] = universe.geometryStructure.pickAll(this, pickShape); + + if ((geomAtoms == null) || (geomAtoms.length == 0)) { + return null; + } + + PickInfo[] pickInfoArr = null; + + if (mode == PickInfo.PICK_GEOMETRY) { + // Need to have closestDistance set + flags |= PickInfo.CLOSEST_DISTANCE; + pickInfoArr= PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL); + if (pickInfoArr != null) { + PickInfo.sortPickInfoArray(pickInfoArr); + } + } + else { + PickInfo.sortGeomAtoms(geomAtoms, pickShape); + pickInfoArr= PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL); + } + + return pickInfoArr; + } + + /** + * Returns a SceneGraphPath which references the pickable item + * which is closest to the origin of pickShape. + *
+ * 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")); + } + + PickInfo pickInfo = pickClosest( PickInfo.PICK_BOUNDS, + PickInfo.SCENEGRAPHPATH, pickShape); + + if(pickInfo == null) { + return null; + } + return pickInfo.getSceneGraphPath(); + } + + /** + * Returns a PickInfo which references the pickable item + * which is closest to the origin of pickShape. + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if this Locale has been + * removed from its VirtualUniverse. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see BranchGroup#pickClosest(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo pickClosest( int mode, int flags, PickShape pickShape ) { + + PickInfo[] pickInfoArr = null; + + pickInfoArr = pickAllSorted( mode, flags, pickShape ); + + if(pickInfoArr == null) { + return null; + } + + return pickInfoArr[0]; + + } + + /** + * Returns a reference to any item that is Pickable below this + * Locale which intersects with pickShape. + * + * @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")); + } + + PickInfo pickInfo = pickAny( PickInfo.PICK_BOUNDS, + PickInfo.SCENEGRAPHPATH, pickShape); + + if(pickInfo == null) { + return null; + } + return pickInfo.getSceneGraphPath(); + + } + + /** + * Returns a PickInfo which references the pickable item below this + * Locale which intersects with pickShape. + * The accuracy of the pick is set by the pick mode. The mode include : + * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned + * is specified via a masked variable, flags, indicating which components are + * present in each returned PickInfo object. + * + * @param mode picking mode, one of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY. + * + * @param flags a mask indicating which components are present in each PickInfo object. + * This is specified as one or more individual bits that are bitwise "OR"ed together to + * describe the PickInfo data. The flags include : + *
    + * PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
    + * PickInfo.NODE - request for computed intersected Node.
    + * PickInfo.LOCAL_TO_VWORLD - request for computed local to virtual world transform.
    + * PickInfo.CLOSEST_INTERSECTION_POINT - request for closest intersection point.
    + * PickInfo.CLOSEST_DISTANCE - request for the distance of closest intersection.
    + * PickInfo.CLOSEST_GEOM_INFO - request for only the closest intersection geometry information.
    + * PickInfo.ALL_GEOM_INFO - request for all intersection geometry information.
    + *
+ * + * @param pickShape the description of this picking volume or area. + * + * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and + * ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode + * is set to PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS + * nor PICK_GEOMETRY. + * + * @exception IllegalArgumentException if pick mode is PICK_BOUNDS + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalArgumentException if pickShape is PickBounds + * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, + * CLOSEST_GEOM_INFO or ALL_GEOM_INFO. + * + * @exception IllegalStateException if this Locale has been + * removed from its VirtualUniverse. + * + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are as follows : + *
    + *
  • By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
  • + *
  • By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
  • + *
  • Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above)
  • + *
+ * + * @see BranchGroup#pickAny(int,int,javax.media.j3d.PickShape) + * @see PickInfo + * + * @since Java 3D 1.4 + * + */ + public PickInfo pickAny( int mode, int flags, PickShape pickShape ) { + + validateModeFlagAndPickShape(mode, flags, pickShape); + GeometryAtom geomAtoms[] = universe.geometryStructure.pickAll(this, pickShape); + + PickInfo[] pickInfoArr = PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ANY); + + if(pickInfoArr == null) { + return null; + } + + return pickInfoArr[0]; + + } + +} diff --git a/src/javax/media/j3d/MRSWLock.java b/src/javax/media/j3d/MRSWLock.java new file mode 100644 index 0000000..a010360 --- /dev/null +++ b/src/javax/media/j3d/MRSWLock.java @@ -0,0 +1,88 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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.err.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/javax/media/j3d/MasterControl.java b/src/javax/media/j3d/MasterControl.java new file mode 100644 index 0000000..0d7af66 --- /dev/null +++ b/src/javax/media/j3d/MasterControl.java @@ -0,0 +1,3748 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * 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.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.logging.Level; +import java.util.logging.Logger; + +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 SET_WORK_FOR_REQUEST_RENDERER = 5; + static final int RUN_RENDERER_CLEANUP = 6; + + // The thread states for MC + static final int SLEEPING = 0; + static final int RUNNING = 1; + static final int WAITING_FOR_THREADS = 3; + static final int WAITING_FOR_CPU = 4; + static final int WAITING_FOR_RENDERER_CLEANUP = 5; + + // 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); + + // Developer logger for reporting informational messages; see getDevLogger() + private static boolean devLoggerEnabled = false; + private static Logger devLogger; + + // Stats logger for reporting runtime statistics; see getStatsLogger() + private static boolean statsLoggerEnabled = false; + private static Logger statsLogger; + + // Core logger for reporting internal errors, warning, and + // informational messages; see getCoreLogger() + private static boolean coreLoggerEnabled = false; + private static Logger coreLogger; + + // Flag indicating that the rendering pipeline libraries are loaded + private static boolean librariesLoaded = false; + + /** + * reference to MasterControl thread + */ + private MasterControlThread mcThread = null; + + /** + * The list of views that are currently registered + */ + private UnorderList views = new UnorderList(1, View.class); + + + /** + * by MIK OF CLASSX + * + * the flag to indicate whether the background of the offscreen + * canvas must be transparent or not false by default + */ + boolean transparentOffScreen = false; + + /** + * Flag to indicate whether Pbuffers are used for off-screen + * rendering; true by default. Set by the "j3d.usePbuffer" + * property, When this flag is set to false, Bitmap (Windows) or + * Pixmap (UNIX) rendering will be used + */ + boolean usePbuffer = true; + + /** + * Flag to indicate whether should renderer view frustum culling is done; + * true by default. + * Set by the -Dj3d.viewFrustumCulling property, When this flag is + * set to false, the renderer view frustum culling is turned off. + */ + boolean viewFrustumCulling = true; + + /** + * 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; + + /** + * 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; + + // Only one Notification thread in the system. + private NotificationThread notificationThread; + + /** + * 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. It is initialized once, the first time + * that a MasterControl object is created. + */ + static long systemStartTime = 0L; + + // This is a time stamp used when context is created + private long contextTimeStamp = 0; + + // This is an array of canvasIds in used + private boolean[] canvasIds = null; + private int canvasFreeIndex = 0; + private Object canvasIdLock = new Object(); + + // This is a counter for rendererBit + private int rendererCount = 0; + + // Flag that indicates whether to shared display context or not + boolean isSharedCtx = 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; + + // 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; + + // If this flag is set, then geometry arrays with vertex attributes can + // be in display list. + boolean vertexAttrsInDisplayList = false; + + // Issue 249 - flag that indicates whether the soleUser optimization is permitted + boolean allowSoleUser = false; + + // Issue 266 - Flag indicating whether null graphics configs are allowed + // Set by -Dj3d.allowNullGraphicsConfig property + // Setting this flag causes Canvas3D to allow a null GraphicsConfiguration + // for on-screen canvases. This is only for backward compatibility with + // legacy applications. + boolean allowNullGraphicsConfig = false; + + // Issue 239 - Flag indicating whether the stencil buffer is cleared by + // default each frame when the color and depth buffers are cleared. + // Note that this is a partial solution, since we eventually want an API + // to control this. + boolean stencilClear = 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 the pre-1.5 behavior of enforcing power-of-two + // textures. If set, then any non-power-of-two textures will throw an + // exception. + boolean enforcePowerOfTwo = false; + + // 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; + + // 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 isCompiledVertexArray = true; + + // Number of reserved vertex attribute locations for GLSL (must be at + // least 1). + // Issue 269 - need to reserve up to 6 vertex attribtue locations to ensure + // that we don't collide with a predefined gl_* attribute on nVidia cards. + int glslVertexAttrOffset = 6; + + // 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 Java 3D threads + private static int threadPriority; + + static private Object mcThreadLock = new Object(); + + private ArrayList timestampUpdateList = new ArrayList(3); + + private UnorderList freeMessageList = new UnorderList(8); + + // Maximum number of lights + int maxLights; + + // Set by the -Dj3d.sortShape3DBounds property, When this flag is + // set to true, the bounds of the Shape3D node will be used in + // place of the computed GeometryArray bounds for transparency + // sorting for those Shape3D nodes whose boundsAutoCompute + // attribute is set to false. + boolean sortShape3DBounds = false; + + //Set by -Dj3d.forceReleaseView property. + //Setting this flag as true disables the bug fix 4267395 in View deactivate(). + //The bug 4267395 can lock-up *some* systems, but the bug fix can + //produce memory leaks in applications which creates and destroy Canvas3D + //from time to time. + //Set as true if you have memory leaks after disposing Canvas3D. + //Default false value does affect Java3D View dispose behavior. + boolean forceReleaseView = false; + + // Issue 480: Cache the bounds of nodes so that getBounds does not + // recompute the boounds of the entire graph per call + boolean cacheAutoComputedBounds = false; + + // issue 544 + boolean useBoxForGroupBounds = false; + + /** + * Constructs a new MasterControl object. Note that there is + * exatly one MasterControl object, created statically by + * VirtualUniverse. + */ + MasterControl() { + assert librariesLoaded; + + // Initialize the start time upon which alpha's and behaviors + // are synchronized to (if it isn't already set). + if (systemStartTime == 0L) { + systemStartTime = J3dClock.currentTimeMillis(); + } + + if(J3dDebug.devPhase) { + // Check to see whether debug mode is allowed + J3dDebug.debug = getBooleanProperty("j3d.debug", false, + "J3dDebug.debug"); + } + + // Check to see whether shared contexts are allowed + isSharedCtx = getBooleanProperty("j3d.sharedctx", isSharedCtx, "shared contexts"); + + doCompaction = getBooleanProperty("j3d.docompaction", doCompaction, + "compaction"); + + // by MIK OF CLASSX + transparentOffScreen = getBooleanProperty("j3d.transparentOffScreen", transparentOffScreen, "transparent OffScreen"); + + usePbuffer = getBooleanProperty("j3d.usePbuffer", + usePbuffer, + "Off-screen Pbuffer"); + + viewFrustumCulling = getBooleanProperty("j3d.viewFrustumCulling", viewFrustumCulling,"View frustum culling in the renderer is"); + + sortShape3DBounds = + getBooleanProperty("j3d.sortShape3DBounds", sortShape3DBounds, + "Shape3D bounds enabled for transparency sorting", + "Shape3D bounds *ignored* for transparency sorting"); + + forceReleaseView = + getBooleanProperty("j3d.forceReleaseView", forceReleaseView, + "forceReleaseView after Canvas3D dispose enabled", + "forceReleaseView after Canvas3D dispose disabled"); + +// FIXME: GL_NV_register_combiners +// useCombiners = getBooleanProperty("j3d.usecombiners", useCombiners, +// "Using NV_register_combiners if available", +// "NV_register_combiners disabled"); + + if (getProperty("j3d.disablecompile") != null) { + disableCompile = true; + System.err.println("Java 3D: BranchGroup.compile disabled"); + } + + if (getProperty("j3d.disableSeparateSpecular") != null) { + disableSeparateSpecularColor = true; + System.err.println("Java 3D: separate specular color disabled if possible"); + } + + isDisplayList = getBooleanProperty("j3d.displaylist", isDisplayList, + "display list"); + + implicitAntialiasing = + getBooleanProperty("j3d.implicitAntialiasing", + implicitAntialiasing, + "implicit antialiasing"); + + isCompiledVertexArray = + getBooleanProperty("j3d.compiledVertexArray", + isCompiledVertexArray, + "compiled vertex array"); + + boolean j3dOptimizeSpace = + getBooleanProperty("j3d.optimizeForSpace", true, + "optimize for space"); + + if (isDisplayList) { + // Build Display list for by-ref geometry + // ONLY IF optimizeForSpace is false + if (!j3dOptimizeSpace) { + buildDisplayListIfPossible = true; + } + + // Build display lists for geometry with vertex attributes + // ONLY if we are in GLSL mode and GLSL shaders are available + vertexAttrsInDisplayList = true; + } + + // Check to see whether Renderer can run without DSI lock + doDsiRenderLock = getBooleanProperty("j3d.renderLock", + doDsiRenderLock, + "render lock"); + + // Check to see whether we enforce power-of-two textures + enforcePowerOfTwo = getBooleanProperty("j3d.textureEnforcePowerOfTwo", + enforcePowerOfTwo, + "checking power-of-two textures"); + + // Issue 249 - check to see whether the soleUser optimization is permitted + allowSoleUser = getBooleanProperty("j3d.allowSoleUser", + allowSoleUser, + "sole-user mode"); + + // Issue 266 - check to see whether null graphics configs are allowed + allowNullGraphicsConfig = getBooleanProperty("j3d.allowNullGraphicsConfig", + allowNullGraphicsConfig, + "null graphics configs"); + + // Issue 239 - check to see whether per-frame stencil clear is enabled + stencilClear = getBooleanProperty("j3d.stencilClear", + stencilClear, + "per-frame stencil clear"); + + // Check to see if stereo mode is sharing the Z-buffer for both eyes. + sharedStereoZBuffer = + getBooleanProperty("j3d.sharedstereozbuffer", + sharedStereoZBuffer, + "shared stereo Z buffer"); + + // Get the maximum number of concurrent threads (CPUs) + final int defaultThreadLimit = getNumberOfProcessors() + 1; + Integer threadLimit = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public Integer 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); + } + + // Get the input device scheduler sampling time + Integer samplingTime = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public Integer 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"); + } + + // Get the glslVertexAttrOffset + final int defaultGLSLVertexAttrOffset = glslVertexAttrOffset; + Integer vattrOffset = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public Integer run() { + return Integer.getInteger("j3d.glslVertexAttrOffset", + defaultGLSLVertexAttrOffset); + } + }); + + glslVertexAttrOffset = vattrOffset.intValue(); + if (glslVertexAttrOffset < 1) { + glslVertexAttrOffset = 1; + } + if (J3dDebug.debug || glslVertexAttrOffset != defaultGLSLVertexAttrOffset) { + System.err.println("Java 3D: glslVertexAttrOffset = " + + glslVertexAttrOffset); + } + + // Issue 480 : Cache bounds returned by getBounds() + cacheAutoComputedBounds = + getBooleanProperty("j3d.cacheAutoComputeBounds", + cacheAutoComputedBounds, + "Cache AutoCompute Bounds, accelerates getBounds()"); + + // Issue 544 + useBoxForGroupBounds = + getBooleanProperty("j3d.useBoxForGroupBounds", + useBoxForGroupBounds, + "Use of BoundingBox for group geometric bounds"); + + // Check for obsolete properties + String[] obsoleteProps = { + "j3d.backgroundtexture", + "j3d.forceNormalized", + "j3d.g2ddrawpixel", + "j3d.simulatedMultiTexture", + "j3d.useFreeLists", + }; + for (int i = 0; i < obsoleteProps.length; i++) { + if (getProperty(obsoleteProps[i]) != null) { + System.err.println("Java 3D: " + obsoleteProps[i] + " property ignored"); + } + } + + // Get the maximum Lights + maxLights = Pipeline.getPipeline().getMaximumLights(); + + // create the freelists + FreeListManager.createFreeLists(); + + // create an array canvas use registers + // The 32 limit can be lifted once the + // resourceXXXMasks in other classes + // are change not to use integer. + canvasIds = new boolean[32]; + for(int i=0; i() { + @Override + public Object run() { + coreLoggerEnabled = initLogger(coreLogger, null); + devLoggerEnabled = initLogger(devLogger, Level.OFF); + statsLoggerEnabled = initLogger(statsLogger, Level.OFF); + return null; + } + }); + } + + /** + * Get the developer logger -- OFF by default + * + * WARNING - for probable incorrect or inconsistent api usage + * INFO - for informational messages such as performance hints (less verbose than FINE) + * FINE - for informational messages from inner loops + * FINER - using default values which may not be optimal + */ + static Logger getDevLogger() { + return devLogger; + } + + static boolean isDevLoggable(Level level) { + return devLoggerEnabled && devLogger.isLoggable(level); + } + + /** + * Get the stats logger -- OFF by default + * + * WARNING - statistical anomalies + * INFO - basic performance stats - not too verbose and minimally intrusive + * FINE - somewhat verbose and intrusive + * FINER - more verbose and intrusive + * FINEST - most verbose and intrusive + */ + static Logger getStatsLogger() { + return statsLogger; + } + + static boolean isStatsLoggable(Level level) { + return statsLoggerEnabled && statsLogger.isLoggable(level); + } + + /** + * Get the core logger -- level is INFO by default + * + * SEVERE - Serious internal errors + * WARNING - Possible internal errors or anomalies + * INFO - General informational messages + * FINE - Internal debugging information - somewhat verbose + * FINER - Internal debugging information - more verbose + * FINEST - Internal debugging information - most verbose + */ + static Logger getCoreLogger() { + return coreLogger; + } + + static boolean isCoreLoggable(Level level) { + return coreLoggerEnabled && coreLogger.isLoggable(level); + } + +private static String getProperty(final String prop) { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public String run() { + return System.getProperty(prop); + } + }); +} + + static boolean getBooleanProperty(String prop, + boolean defaultValue, + String trueMsg, + String falseMsg) { + boolean value = defaultValue; + String propValue = getProperty(prop); + + if (propValue != null) { + value = Boolean.valueOf(propValue).booleanValue(); + if (J3dDebug.debug) + System.err.println("Java 3D: " + (value ? trueMsg : falseMsg)); + } + return value; + } + + static boolean getBooleanProperty(String prop, + boolean defaultValue, + String msg) { + return getBooleanProperty(prop, + defaultValue, + (msg + " enabled"), + (msg + " disabled")); + } + + /** + * Method to create and initialize the rendering Pipeline object, + * and to load the native libraries needed by Java 3D. This is + * called by the static initializer in VirtualUniverse before + * the MasterControl object is created. + */ + static void loadLibraries() { + assert !librariesLoaded; + + // Initialize the Pipeline object associated with the + // renderer specified by the "j3d.rend" system property. + // + // XXXX : We should consider adding support for a more flexible, + // dynamic selection scheme via an API call. + + // Default rendering pipeline is the JOGL pipeline + Pipeline.Type pipelineType = Pipeline.Type.JOGL; + + final String rendStr = getProperty("j3d.rend"); + if (rendStr == null) { + // Use default pipeline + } else if (rendStr.equals("jogl")) { + pipelineType = Pipeline.Type.JOGL; + } else if (rendStr.equals("noop")) { + pipelineType = Pipeline.Type.NOOP; + } else { + System.err.println("Java 3D: Unrecognized renderer: " + rendStr); + // Use default pipeline + } + + // Construct the singleton Pipeline instance + Pipeline.createPipeline(pipelineType); + + librariesLoaded = true; + } + + + /** + * Invoke from InputDeviceScheduler to create an + * InputDeviceBlockingThread. + */ + InputDeviceBlockingThread getInputDeviceBlockingThread( + final InputDevice device) { + + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public InputDeviceBlockingThread run() { + synchronized (rootThreadGroup) { + InputDeviceBlockingThread 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() { + @Override + 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 the a unused renderer bit + */ + int getRendererId() { + return 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); + } + + int getCanvasId() { + int i; + + synchronized(canvasIdLock) { + // Master control need to keep count itself + for(i=canvasFreeIndex; i= canvasIds.length) { + throw new RuntimeException("Cannot render to more than 32 Canvas3Ds"); + } + + canvasIds[i] = true; + canvasFreeIndex = i + 1; + } + + return i; + + } + + void freeCanvasId(int canvasId) { + // Valid range is [0, 31] + synchronized(canvasIdLock) { + + canvasIds[canvasId] = false; + if(canvasFreeIndex > canvasId) { + canvasFreeIndex = canvasId; + } + } + } + + + /** + * Create a Renderer if it is not already done so. + * This is used for GraphicsConfigTemplate3D passing + * graphics call to RequestRenderer, and for creating + * an off-screen buffer for an off-screen Canvas3D. + */ + private Renderer createRenderer(GraphicsConfiguration gc) { + final GraphicsDevice gd = gc.getDevice(); + + Renderer rdr = Screen3D.deviceRendererMap.get(gd); + if (rdr != null) { + return rdr; + } + + + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + 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 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; + } + if (notificationThread != null) { + notificationThread.finish(); + notificationThread = null; + } + requestObjList.clear(); + requestTypeList.clear(); + return true; + } + running = true; + createMCThreads(); + return false; + } + } + } + + /** + * This method increments and returns the next time value + * timeLock must get before this procedure is invoked + */ + final long getTime() { + return (time++); + } + + + /** + * 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(); + } + + /** + * This takes the specified notification message and sends it to the + * notification thread for processing. + */ + void sendNotification(J3dNotification notification) { + notificationThread.addNotification(notification); + } + + /** + * Create and start the MasterControl Thread. + */ + void createMasterControlThread() { + // Issue 364: don't create master control thread if already created + if (mcThread != null) { + return; + } + + running = true; + workToDo = true; + state = RUNNING; + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + 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; + + if (isCoreLoggable(Level.FINEST)) { + dumpMessage("sendMessage", 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 = 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(); + } + } + } + + + /** + * 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. + * Note that this method is called without the timeLock held. + * We must synchronize on timeLock to prevent updating + * thread.lastUpdateTime from user thread in sendMessage() + * or sendRunMessage(). + */ + private void updateTimeValues() { + synchronized (timeLock) { + 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 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 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 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; + J3dThreadData lastRunThread = null; + waitTimestamp++; + sleepTime = 0L; + + boolean threadToRun = false; // Not currently used + + // Fix for Issue 12: loop through the list of threads, calling + // computeCycleTime() exactly once per view. This ensures that + // all threads for a given view see consistent values for + // isMinCycleTimeAchieve and sleepTime. + v = null; + for (i=0; i sleepTime) { + sleepTime = thread.view.sleepTime; + } + } + v = thread.view; + } + + v = null; + for (i=0; i thread.lastRunTime) && + !thread.thread.userStop) { + + 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; + } + } + + // Issue 275 - go to sleep without holding timeLock + // Sleep for the amount of time needed to satisfy the minimum + // cycle time for all views. + if (sleepTime > 0) { + // System.err.println("MasterControl: sleep(" + sleepTime + ")"); + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + System.err.println(e); + } + // System.err.println("MasterControl: done sleeping"); + } + } + + private void createUpdateThread(J3dStructure structure) { + final J3dStructure s = structure; + + if (s.updateThread == null) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + 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; + + 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() { + @Override + 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; + + 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 + ArrayList list = new ArrayList(); + for (Enumeration e = PhysicalEnvironment.physicalEnvMap.keys(); e.hasMoreElements();) { + PhysicalEnvironment phyEnv = e.nextElement(); + InputDeviceScheduler sched = PhysicalEnvironment.physicalEnvMap.get(phyEnv); + boolean phyEnvHasUser = false; + for (int i = 0; i < phyEnv.users.size(); i++) { + if (views.contains(phyEnv.users.get(i))) { + // at least one registered view refer to it. + phyEnvHasUser = true; + break; + } + } + + if (!phyEnvHasUser) { + if (J3dDebug.devPhase) { + J3dDebug.doDebug(J3dDebug.masterControl, J3dDebug.LEVEL_1, + "MC: Destroy InputDeviceScheduler thread " + + sched); + } + sched.finish(); + phyEnv.inputsched = null; + list.add(phyEnv); + } + } + for (int i = 0; i < list.size(); 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 = 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(); ) { + e.nextElement().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(); ) { + e.nextElement().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() { + @Override + public Object run() { + synchronized (rootThreadGroup) { + timerThread = new TimerThread(rootThreadGroup); + timerThread.setPriority(threadPriority); + } + return null; + } + }); + timerThread.start(); + + // Create notification thread + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public Object run() { + synchronized (rootThreadGroup) { + notificationThread = new NotificationThread(rootThreadGroup); + notificationThread.setPriority(threadPriority); + } + return null; + } + }); + notificationThread.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; + } + if (notificationThread != null) { + notificationThread.finish(); + notificationThread = null; + } + + // shouldn't all of these be synchronized ??? + synchronized (VirtualUniverse.mc.deviceScreenMap) { + deviceScreenMap.clear(); + } + + mirrorObjects.clear(); + // Note: We should not clear the DISPLAYLIST/TEXTURE + // list here because other structure may release them + // later + + for(int i=0; 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 = Screen3D.deviceRendererMap.get(cv.screen.graphicsDevice); + if (rdr == null) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public Object run() { + + synchronized (rootThreadGroup) { + screen.renderer + = new Renderer( + rootThreadGroup); + screen.renderer.setPriority(threadPriority); + } + return null; + } + }); + screen.renderer.initialize(); + Screen3D.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 + // + // Issue 131: Automatic offscreen canvases need to + // be added to onscreen list. Special case. + // + // TODO KCR Issue 131: this should probably be + // changed to a list of screens since multiple + // off-screen canvases (either auto or manual) can + // be used by the same renderer + if (!cv.manualRendering) { + 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() { + @Override + 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=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 e = Screen3D.deviceRendererMap.elements(); + e.hasMoreElements(); ) { + Renderer rdr = 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=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) { + // Issue 81: We need to process this message as long + // as there are no views associated with this + // universe. Previously, this message was ignored if + // there were views associated with *any* universe, + // which led to a memory / thread leak. + boolean foundView = false; + VirtualUniverse univ = (VirtualUniverse) o; + View v[] = (View []) views.toArray(false); + for (int j = views.size() - 1; j >= 0; j--) { + if (v[j].universe == univ) { + foundView = true; + break; + } + } + if (!foundView) { + destroyUniverseThreads(univ); + threadListsChanged = true; + } + } else if (type == START_RENDERER) { + if (o instanceof Canvas3D) { + Canvas3D c3d = (Canvas3D) o; + if (!c3d.isFatalError()) { + c3d.isRunningStatus = true; + } + } else { + ((Renderer) o).userStop = false; + } + 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) { + Pipeline.getPipeline().freeDrawingSurfaceNative(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 = ((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 || requestRenderWorkToDo) { + 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 = J3dClock.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 = timestampUpdateList.get(k); + v.setFrameTimingValues(); + v.universe.behaviorStructure.incElapsedFrames(); + } + timestampUpdateList.clear(); + updateMirrorObjects(); + done = true; + + if (isStatsLoggable(Level.INFO)) { + // Instrumentation of Java 3D renderer + logTimes(); + } + } + } + break; + + case THREAD_DONE: + if (state != WAITING_FOR_RENDERER_CLEANUP) { + + threadPending--; + assert threadPending >= 0 : ("threadPending = " + threadPending); + if (nthread.type == J3dThread.RENDER_THREAD) { + View v = (View) nthread.args[3]; + if (v != null) { // STOP_TIMER + v.stopTime = J3dClock.currentTimeMillis(); + } + + if (--renderPending == 0) { + renderWaiting = false; + } + assert renderPending >= 0 : ("renderPending = " + renderPending); + } else { + if (--statePending == 0) { + stateWaiting = false; + } + assert statePending >= 0 : ("statePending = " + statePending); + } + if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS) { + notify(); + } + } else { + notify(); + state = RUNNING; + } + break; + + case CHECK_FOR_WORK: + if (!workToDo) { + state = SLEEPING; + // NOTE: this could wakeup spuriously (see issue 279), but it + // will not cause any problems. + 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; + workToDo = true; + if (state == WAITING_FOR_CPU || state == WAITING_FOR_THREADS || + state == SLEEPING) { + notify(); + } + break; + + case RUN_RENDERER_CLEANUP: + nthread.runMonitor(J3dThread.RUN, currentTime, + rendererCleanupArgs); + state = WAITING_FOR_RENDERER_CLEANUP; + // Issue 279 - loop until state is set to running + while (state != RUNNING) { + try { + wait(); + } catch (InterruptedException e) { + System.err.println(e); + } + } + break; + + default: + // Should never get here + assert false : "missing case in switch statement"; + } + } + + // Static initializer + static { + // create ThreadGroup + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + 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; + } + }); + + // Initialize loggers + try { + initLoggers(); + } catch (RuntimeException ex) { + System.err.println(ex); + } + } + + + 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", + "BEHAVIOR_REEVALUATE", + "CREATE_OFFSCREENBUFFER", + "DESTROY_CTX_AND_OFFSCREENBUFFER", + "SHADER_ATTRIBUTE_CHANGED", + "SHADER_ATTRIBUTE_SET_CHANGED", + "SHADER_APPEARANCE_CHANGED", + "ALLOCATE_CANVASID", + "FREE_CANVASID", + }; + + private String dumpThreads(int threads) { + StringBuffer strBuf = new StringBuffer(); + strBuf.append("threads:"); + //dump Threads type + if ((threads & J3dThread.BEHAVIOR_SCHEDULER) != 0) { + strBuf.append(" BEHAVIOR_SCHEDULER"); + } + if ((threads & J3dThread.SOUND_SCHEDULER) != 0) { + strBuf.append(" SOUND_SCHEDULER"); + } + if ((threads & J3dThread.INPUT_DEVICE_SCHEDULER) != 0) { + strBuf.append(" INPUT_DEVICE_SCHEDULER"); + } + if ((threads & J3dThread.RENDER_THREAD) != 0) { + strBuf.append(" RENDER_THREAD"); + } + if ((threads & J3dThread.UPDATE_GEOMETRY) != 0) { + strBuf.append(" UPDATE_GEOMETRY"); + } + if ((threads & J3dThread.UPDATE_RENDER) != 0) { + strBuf.append(" UPDATE_RENDER"); + } + if ((threads & J3dThread.UPDATE_BEHAVIOR) != 0) { + strBuf.append(" UPDATE_BEHAVIOR"); + } + if ((threads & J3dThread.UPDATE_SOUND) != 0) { + strBuf.append(" UPDATE_SOUND"); + } + if ((threads & J3dThread.UPDATE_RENDERING_ATTRIBUTES) != 0) { + strBuf.append(" UPDATE_RENDERING_ATTRIBUTES"); + } + if ((threads & J3dThread.UPDATE_RENDERING_ENVIRONMENT) != 0) { + strBuf.append(" UPDATE_RENDERING_ENVIRONMENT"); + } + if ((threads & J3dThread.UPDATE_TRANSFORM) != 0) { + strBuf.append(" UPDATE_TRANSFORM"); + } + + return strBuf.toString(); + } + + // Method to log the specified message. Note that the caller + // should check for isCoreLoggable(FINEST) before calling + private void dumpMessage(String str, J3dMessage m) { + StringBuffer strBuf = new StringBuffer(); + strBuf.append(str).append(" "); + if (m.type >= 0 && m.type < mtype.length) { + strBuf.append(mtype[m.type]); + } else { + strBuf.append(""); + } + strBuf.append(" ").append(dumpThreads(m.threads)); + getCoreLogger().finest(strBuf.toString()); + } + + + 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 Thread.yield(), 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 + } + } + + // Return the number of available processors + private int getNumberOfProcessors() { + return Runtime.getRuntime().availableProcessors(); + } + + // + // The following framework supports code instrumentation. To use it, + // add code of the following form to areas that you want to enable for + // timing: + // + // long startTime = System.nanoTime(); + // sortTransformGroups(tSize, tgs); + // long deltaTime = System.nanoTime() - startTime; + // VirtualUniverse.mc.recordTime(MasterControl.TimeType.XXXXX, deltaTime); + // + // where "XXXXX" is the enum representing the code segment being timed. + // Additional enums can be defined for new subsystems. + // + + static enum TimeType { + TOTAL_FRAME, + RENDER, + BEHAVIOR, + // TRANSFORM_UPDATE, + // ... + } + + private long[] statTimes = new long[TimeType.values().length]; + private int[] statCounts = new int[TimeType.values().length]; + private boolean[] statSeen = new boolean[TimeType.values().length]; + private int frameCycleTick = 0; + private long frameCycleNumber = 0L; + + // Method to record times -- should not be called unless the stats logger + // level is set to INFO or lower + synchronized void recordTime(TimeType type, long deltaTime) { + int idx = type.ordinal(); + statTimes[idx] += deltaTime; + statCounts[idx]++; + statSeen[idx] = true; + } + + // Method to record times -- this is not called unless the stats logger + // level is set to INFO or lower + private synchronized void logTimes() { + ++frameCycleNumber; + if (++frameCycleTick >= 10) { + StringBuffer strBuf = new StringBuffer(); + strBuf.append("----------------------------------------------\n"). + append(" Frame Number = "). + append(frameCycleNumber). + append("\n"); + for (int i = 0; i < statTimes.length; i++) { + if (statSeen[i]) { + strBuf.append(" "); + if (statCounts[i] > 0) { + strBuf.append(TimeType.values()[i]). + append(" ["). + append(statCounts[i]). + append("] = "). + append((double)statTimes[i] / 1000000.0 / (double)statCounts[i]). + append(" msec per call\n"); + statTimes[i] = 0L; + statCounts[i] = 0; + } else { + assert statTimes[i] == 0L; + strBuf.append(TimeType.values()[i]). + append(" [0] = 0.0 msec\n"); + } + } + } + getStatsLogger().info(strBuf.toString()); + frameCycleTick = 0; + } + } + +} diff --git a/src/javax/media/j3d/MasterControlThread.java b/src/javax/media/j3d/MasterControlThread.java new file mode 100644 index 0000000..f1c67b0 --- /dev/null +++ b/src/javax/media/j3d/MasterControlThread.java @@ -0,0 +1,76 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 { + + private static int numInstances = 0; + private int instanceNum = -1; + + private static synchronized int newInstanceNum() { + return (++numInstances); + } + + private int getInstanceNum() { + if (instanceNum == -1) + instanceNum = newInstanceNum(); + return instanceNum; + } + + MasterControlThread(ThreadGroup threadGroup) { + super(threadGroup, ""); + setName("J3D-MasterControl-" + getInstanceNum()); + VirtualUniverse.mc.createMCThreads(); + this.start(); + } + + @Override + 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/javax/media/j3d/Material.java b/src/javax/media/j3d/Material.java new file mode 100644 index 0000000..528aa0e --- /dev/null +++ b/src/javax/media/j3d/Material.java @@ -0,0 +1,717 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 null, + * lighting is disabled for all nodes that use that Appearance object. + *

+ * The properties that can be set for a Material object are: + *

    + *
  • 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).

  • + *
  • 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).

  • + *
  • 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).

  • + *
  • 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).

  • + *
  • 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.

  • + *
  • 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.

  • + *
+ * + * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_COMPONENT_READ + }; + + /** + * Constructs and initializes a Material object using default parameters. + * The default values are as follows: + *
    + * lighting enable : true
    + * ambient color : (0.2, 0.2, 0.2)
    + * emmisive color : (0.0, 0.0, 0.0)
    + * diffuse color : (1.0, 1.0, 1.0)
    + * specular color : (1.0, 1.0, 1.0)
    + * shininess : 64
    + * color target : DIFFUSE + *
+ */ + public Material() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((MaterialRetained)this.retained).createMaterial(ambientColor, + emissiveColor, diffuseColor, specularColor, + shininess); + } + + /** + * Creates a retained mode MaterialRetained object that this + * Material component object will point to. + */ + @Override + 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. + */ + @Override + public String toString() { + StringBuffer str = new StringBuffer(getNamePrefix()); + str.append("javax.media.j3d.Material: "); + 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) + */ + @Override + public NodeComponent cloneNodeComponent() { + Material m = new Material(); + m.duplicateNodeComponent(this); + return m; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/MaterialRetained.java b/src/javax/media/j3d/MaterialRetained.java new file mode 100644 index 0000000..f900eee --- /dev/null +++ b/src/javax/media/j3d/MaterialRetained.java @@ -0,0 +1,564 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3f; + +/** + * 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; + } + + @Override + 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. + */ + void updateNative(Context ctx, + float red, float green, float blue, float alpha, + boolean enableLighting) { + Pipeline.getPipeline().updateMaterial(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 + */ + @Override + synchronized void initMirrorObject() { + MaterialRetained mirrorMaterial = (MaterialRetained)mirror; + mirrorMaterial.set(this); + } + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 + @Override + 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 = new J3dMessage(); + 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 gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + @Override + void handleFrequencyChange(int bit) { + if (bit == Material.ALLOW_COMPONENT_WRITE) { + setFrequencyChangeMask(Material.ALLOW_COMPONENT_WRITE, 0x1); + } + } +} + diff --git a/src/javax/media/j3d/MediaContainer.java b/src/javax/media/j3d/MediaContainer.java new file mode 100644 index 0000000..3a76b07 --- /dev/null +++ b/src/javax/media/j3d/MediaContainer.java @@ -0,0 +1,354 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.io.InputStream; +import java.net.URL; + +/** + * 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 + * setURLString, setURLObject, + * or setInputStream 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_CACHE_READ, + ALLOW_URL_READ + }; + + /** + * Constructs a MediaContainer object with default parameters. + * The default values are as follows: + *
    + * URL String data : null
    + * URL object data : null
    + * InputStream data : null
    + * cache enable : true
    + *
+ */ + public MediaContainer() { + // Just use default values + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((MediaContainerRetained)this.retained).setInputStream(stream); + } + + /** + * Creates the retained mode MediaContainerRetained object that this + * component object will point to. + */ + @Override + 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 + * setURLString + */ + 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 + * setURLObject + */ + 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 + * getURLString + */ + 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 + * cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + MediaContainer mc = new MediaContainer(); + mc.duplicateNodeComponent(this); + return mc; + } + + + /** + * Copies all MediaContainer information from + * originalNodeComponent into + * the current node. This method is called from the + * cloneNodeComponent method and duplicateNodeComponent + * method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNodeComponent the original node component to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node component's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/MediaContainerRetained.java b/src/javax/media/j3d/MediaContainerRetained.java new file mode 100644 index 0000000..4b89609 --- /dev/null +++ b/src/javax/media/j3d/MediaContainerRetained.java @@ -0,0 +1,206 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.io.InputStream; +import java.net.URL; + +/** + * 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) { + // XXXX: 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 = new J3dMessage(); + 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/javax/media/j3d/MemoryFreeList.java b/src/javax/media/j3d/MemoryFreeList.java new file mode 100644 index 0000000..95de4d4 --- /dev/null +++ b/src/javax/media/j3d/MemoryFreeList.java @@ -0,0 +1,277 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * 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.err.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.err.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.err.println("spaceUsed = " + spaceUsed + " currBlockSize = " + +// currBlockSize + " currBlockIndex = " + +// currBlockIndex + " currBlock = " + currBlock); + if ((currBlockIndex == -1) || (spaceUsed >= currBlockSize)) { + currBlockIndex++; + currBlock = elementData.get(currBlockIndex); + currBlockSize = currBlock.length; + spaceUsed = 0; + } + int index = spaceUsed++; + currBlock[index] = o; + size++; + + return true; + } + } + + protected synchronized Object removeLastElement() { +// System.err.println("removeLastElement: size = " + size); + int index = --spaceUsed; +// System.err.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 = elementData.get(currBlockIndex); + currBlockSize = currBlock.length; + } + spaceUsed = currBlockSize; + } + + return elm; + } + + + synchronized void shrink() { +// System.err.println("shrink size = " + size + " minSize = " + +// minSize); + if ((minSize > minBlockSize) && (numBlocks > 1)) { + justShrunk = true; + +// System.err.println("removing a block"); +// Runtime r = Runtime.getRuntime(); +// r.gc(); +// System.err.println("numBlocks = " + numBlocks + " size = " + size); +// System.err.println("free memory before shrink: " + r.freeMemory()); + + // remove the last block + Object[] block = 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 = elementData.get(currBlockIndex); + currBlockSize = currBlock.length; + + spaceUsed = currBlockSize; + + } + +// r.gc(); +// System.err.println("free memory after shrink: " + r.freeMemory()); +// System.err.println("numBlocks = " + numBlocks + " size = " + size); + } + else { + justShrunk = false; + } + minSize = size; + } + + synchronized void ensureCapacity(int minCapacity) { +// System.err.println("ensureCapacity: size = " + size + " capacity: " + +// elementData.length); +// System.err.println("minCapacity = " + minCapacity + " capacity = " +// + capacity); + + if (minCapacity > capacity) { +// System.err.println("adding a block: numBlocks = " + numBlocks); + int lastBlockSize = elementData.get(numBlocks - 1).length; + int prevBlockSize = 0; + if (numBlocks > 1) { + prevBlockSize = 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.err.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.err.println(e); + return null; + } + } + } + +} + diff --git a/src/javax/media/j3d/ModelClip.java b/src/javax/media/j3d/ModelClip.java new file mode 100644 index 0000000..e03c8a0 --- /dev/null +++ b/src/javax/media/j3d/ModelClip.java @@ -0,0 +1,737 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Enumeration; + +import javax.vecmath.Vector4d; + +/** + * 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. + *

+ * 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). + *

+ * 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. + *

+ * The individual planes specify a half-space defined by the equation: + *

    + * Ax + By + Cz + D <= 0 + *
+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SCOPE_READ, + ALLOW_ENABLE_READ, + ALLOW_INFLUENCING_BOUNDS_READ, + ALLOW_PLANE_READ + }; + + /** + * Constructs a ModelClip node with default parameters. The default + * values are as follows: + *
    + * planes[0] : x <= 1 (1,0,0,-1)
    + * planes[1] : -x <= 1 (-1,0,0,-1)
    + * planes[2] : y <= 1 (0,1,0,-1)
    + * planes[3] : -y <= 1 (0,-1,0,-1)
    + * planes[4] : z <= 1 (0,0,1,-1)
    + * planes[5] : -z <= 1 (0,0,-1,-1)
    + * enables : all planes enabled
    + * scope : empty (universe scope)
    + * influencing bounds : null
    + * influencing bounding leaf : null
    + *
+ */ + public ModelClip() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + } + + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ((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. + */ + @Override + void createRetained() { + this.retained = new ModelClipRetained(); + this.retained.setSource(this); + } + + + + /** + * Used to create a new instance of the node. This routine is called + * by cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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(elm.nextElement()); + } + + // correct value will set in updateNodeReferences + rt.initInfluencingBoundingLeaf(attr.getInfluencingBoundingLeaf()); + } +} diff --git a/src/javax/media/j3d/ModelClipRetained.java b/src/javax/media/j3d/ModelClipRetained.java new file mode 100644 index 0000000..04b248d --- /dev/null +++ b/src/javax/media/j3d/ModelClipRetained.java @@ -0,0 +1,1043 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Vector4d; + +/** + * 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 = 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 = scopes.elementAt(index); + group.removeMclipScope(); + scopes.removeElementAt(index); + + } + + void removeScope(int index) { + + Object[] scopeInfo = new Object[3]; + ArrayList removeScopeList = new ArrayList(); + GroupRetained group = 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 = 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)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((Group)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(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. + */ + void update(Canvas3D cv, int enableMask) { + cv.setModelViewMatrix(cv.ctx, + cv.vworldToEc.mat, + getLastLocalToVworld()); + update(cv.ctx, enableMask, getLastLocalToVworld()); + } + +void update(Context ctx, int enableMask, Transform3D trans) { + for (int i = 0; i < 6; i++) { + Vector4d v = xformPlanes[i]; + boolean enable = ((enableMask & (1 << i)) != 0); + Pipeline.getPipeline().updateModelClip(ctx, i, enable, + v.x, v.y, v.z, v.w); + } +} + + 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] ; + + } + + + + @Override + 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(); + + if ((component & BOUNDINGLEAF_CHANGED) != 0) { + mirrorModelClip.boundingLeaf = (BoundingLeafRetained)objs[2]; + if (objs[2] != null) { + mirrorModelClip.region = mirrorModelClip.boundingLeaf.transformedRegion; + } + else { + if (mirrorModelClip.regionOfInfluence != null) { + mirrorModelClip.region = 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 = 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 + */ + @Override + 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; + } + } + } + + + @Override + 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 = 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 = new J3dMessage(); + 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 = 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 + */ + @Override + 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 = new J3dMessage(); + 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 = 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]; + + 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 + @Override + 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 = new J3dMessage(); + 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); + } + + @Override + 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]); + } + } + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + leafList.add(mirrorModelClip); + } +} diff --git a/src/javax/media/j3d/Morph.java b/src/javax/media/j3d/Morph.java new file mode 100644 index 0000000..9fc7d9e --- /dev/null +++ b/src/javax/media/j3d/Morph.java @@ -0,0 +1,705 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Hashtable; + +/** + * 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. + * + *

+ * The following restrictions apply to each GeometryArray object + * in the specified array of GeometryArray objects: + * + *

    + *
  • + * All N geometry arrays must be of the + * same type (that is, the same subclass of GeometryArray). + *
  • + * + *

    + *

  • + * The vertexFormat, texCoordSetCount, and validVertexCount must be + * the same for all N geometry arrays. + *
  • + * + *

    + *

  • + * The texCoordSetMap array must be identical (element-by-element) for + * all N geometry arrays. + *
  • + * + *

    + *

  • + * For IndexedGeometryArray objects, the validIndexCount must be the same + * for all N geometry arrays. + *
  • + * + *

    + *

  • + * For GeometryStripArray objects, the stripVertexCounts array must + * be identical (element-by-element) for all N geometry arrays. + *
  • + * + *

    + *

  • + * For IndexedGeometryStripArray objects, the stripIndexCounts array must + * be identical (element-by-element) for all N geometry arrays. + *
  • + * + *

    + *

  • + * For indexed geometry by-reference, the array lengths of each + * enabled vertex component (coord, color, normal, texcoord) + * must be the same for all N geometry arrays. + *
  • + *
+ * + *

+ * For IndexedGeometryArray objects, the vertex arrays are morphed + * before the indexes are applied. Only the indexes in the + * first geometry array (geometry[0]) are used when rendering the + * geometry. + * + * @deprecated As of Java 3D version 1.4. + */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_GEOMETRY_ARRAY_READ, + ALLOW_APPEARANCE_READ, + ALLOW_WEIGHTS_READ, + ALLOW_COLLISION_BOUNDS_READ, + ALLOW_APPEARANCE_OVERRIDE_READ + }; + + // non public default constructor + Morph() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs and initializes a Morph node with the specified array + * of GeometryArray objects. Default values are used for all other + * parameters as follows: + *

    + * appearance : null
    + * weights : [1, 0, 0, 0, ...]
    + * collision bounds : null
    + * appearance override enable : false
    + *

+ * 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: + *

    + *
  • Type of geometry array object (subclass of GeometryArray)
  • + *
  • vertexFormat
  • + *
  • texCoordSetCount
  • + *
  • texCoordSetMap
  • + *
  • validVertexCount
  • + *
  • validIndexCount, for IndexedGeometryArray objects
  • + *
  • stripVertexCounts array, for GeometryStripArray objects
  • + *
  • stripIndexCounts array, for IndexedGeometryStripArray objects
  • + *
  • the array lengths of each enabled vertex component + * (coord, color, normal, texcoord), + * for indexed geometry by-reference
  • + *
+ * + * @exception UnsupportedOperationException if the specified + * geometry arrays contain vertex attributes (that is, if their + * vertexFormat includes the VERTEX_ATTRIBUTES flag). + */ + public Morph(GeometryArray geometryArrays[]) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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: + *
    + *
  • Type of geometry array object (subclass of GeometryArray)
  • + *
  • vertexFormat
  • + *
  • texCoordSetCount
  • + *
  • texCoordSetMap
  • + *
  • validVertexCount
  • + *
  • validIndexCount, for IndexedGeometryArray objects
  • + *
  • stripVertexCounts array, for GeometryStripArray objects
  • + *
  • stripIndexCounts array, for IndexedGeometryStripArray objects
  • + *
  • the array lengths of each enabled vertex component + * (coord, color, normal, texcoord), + * for indexed geometry by-reference
  • + *
+ * + * @exception UnsupportedOperationException if the specified + * geometry arrays contain vertex attributes (that is, if their + * vertexFormat includes the VERTEX_ATTRIBUTES flag). + */ + public Morph(GeometryArray geometryArrays[], Appearance appearance) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((MorphRetained)retained).setGeometryArrays(geometryArrays); + ((MorphRetained)this.retained).setAppearance(appearance); + } + + /** + * Creates the retained mode MorphRetained object that this + * Morph object will point to. + */ + @Override + 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 + *

+ * + * @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: + *

    + *
  • Type of geometry array object (subclass of GeometryArray)
  • + *
  • vertexFormat
  • + *
  • texCoordSetCount
  • + *
  • texCoordSetMap
  • + *
  • validVertexCount
  • + *
  • validIndexCount, for IndexedGeometryArray objects
  • + *
  • stripVertexCounts array, for GeometryStripArray objects
  • + *
  • stripIndexCounts array, for IndexedGeometryStripArray objects
  • + *
  • the array lengths of each enabled vertex component + * (coord, color, normal, texcoord), + * for indexed geometry by-reference
  • + *
+ * + * @exception UnsupportedOperationException if the specified + * geometry arrays contain vertex attributes (that is, if their + * vertexFormat includes the VERTEX_ATTRIBUTES flag). + */ + 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 weights 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Morph m = new Morph(); + m.duplicateNode(this, forceDuplicate); + return m; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * Morph + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + /** + * Copies all Morph information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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); + int numGeometryArrays = morphR.getNumGeometryArrays(); + for (int i = 0; i < numGeometryArrays; i++) { + if (!morphR.geometryArrays[i].source. + getCapability(Geometry.ALLOW_INTERSECT)) { + + throw new CapabilityNotSetException(J3dI18N.getString("Morph6")); + } + } + } + +} diff --git a/src/javax/media/j3d/MorphRetained.java b/src/javax/media/j3d/MorphRetained.java new file mode 100644 index 0000000..9ba5d71 --- /dev/null +++ b/src/javax/media/j3d/MorphRetained.java @@ -0,0 +1,1975 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color3b; +import javax.vecmath.Color3f; +import javax.vecmath.Color4b; +import javax.vecmath.Color4f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.TexCoord2f; +import javax.vecmath.TexCoord3f; +import javax.vecmath.Vector3f; + +/** + * 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[]; + + private 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((Bounds)null); + } + + /** + * 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 = new J3dMessage(); + 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 + */ + @Override + void setBounds(Bounds bounds) { + super.setBounds(bounds); + if (source.isLive() && !boundsAutoCompute) { + J3dMessage message = new J3dMessage(); + 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 the first one for vertex attributes + geo = (GeometryArrayRetained)geometryArrays[0].retained; + if ((geo.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) { + throw new UnsupportedOperationException(J3dI18N.getString("MorphRetained9")); + } + + // 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 = new J3dMessage(); + 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=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 = new J3dMessage(); + 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 + */ + @Override + 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(); + } + } + + @Override + 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. + */ + @Override + void computeCombineBounds(Bounds bounds) { + + if(boundsAutoCompute) { + GeometryArrayRetained mga = + (GeometryArrayRetained)morphedGeometryArray.retained; + if (mga != null) { + synchronized(mga.geoBounds) { + bounds.combine(mga.geoBounds); + } + } + } else { + // Should this be lock too ? ( MT safe ? ) + synchronized(localBounds) { + bounds.combine(localBounds); + } + } + } + + // Return the number of geometry arrays in this MorphRetained object. + int getNumGeometryArrays() { + return numGeometryArrays; + } + + // 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 + */ + @Override + 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 = 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.err.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. + */ + @Override + 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 l = s.lights.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addLight(l.get(m)); + } + } + } + + // Add any scoped fog + if (s.fogs != null) { + ArrayList l = s.fogs.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addFog(l.get(m)); + } + } + } + + // Add any scoped modelClip + if (s.modelClips != null) { + ArrayList l = s.modelClips.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addModelClip(l.get(m)); + } + } + } + + // Add any scoped alt app + if (s.altAppearances != null) { + ArrayList l = s.altAppearances.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addAltApp(l.get(m)); + } + } + } + + if (s.viewLists != null) + shape.viewList = 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 = 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 = 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 = s.lights.get(0); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addLight(l.get(m)); + } + } + } + + // Add any scoped fog + if (s.fogs != null) { + ArrayList l = s.fogs.get(0); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addFog(l.get(m)); + } + } + } + + // Add any scoped modelClip + if (s.modelClips != null) { + ArrayList l = s.modelClips.get(0); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addModelClip(l.get(m)); + } + } + } + + // Add any scoped alt app + if (s.altAppearances != null) { + ArrayList l = s.altAppearances.get(0); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addAltApp(l.get(m)); + } + } + } + + if (s.viewLists != null) + shape.viewList = 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 = 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 = new J3dMessage(); + 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. + */ + @Override + 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=0) { + return (Shape3DRetained) mirrorShape3D.get(i); + } + + // Not possible + throw new RuntimeException("Shape3DRetained: MirrorShape Not found!"); + } + + @Override + 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); + + } + + @Override + 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 = new J3dMessage(); + 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); + } + } + } + + @Override + void updateBounds() { + localBounds = getEffectiveBounds(); + if (source.isLive()) { + J3dMessage message = new J3dMessage(); + 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.USE_COORD_INDEX_ONLY) == 0) { + 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, + 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); + + } + + @Override + void compile(CompileState compState) { + + super.compile(compState); + + // XXXX: 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")); + } + } + } + } + + @Override + 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; + } + } + } + + @Override + void searchGeometryAtoms(UnorderList list) { + list.add(Shape3DRetained.getGeomAtom( + (Shape3DRetained) mirrorShape3D.get(0))); + } +} + + + + + diff --git a/src/javax/media/j3d/MultipleParentException.java b/src/javax/media/j3d/MultipleParentException.java new file mode 100644 index 0000000..481fa16 --- /dev/null +++ b/src/javax/media/j3d/MultipleParentException.java @@ -0,0 +1,51 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/NioImageBuffer.java b/src/javax/media/j3d/NioImageBuffer.java new file mode 100644 index 0000000..899b45a --- /dev/null +++ b/src/javax/media/j3d/NioImageBuffer.java @@ -0,0 +1,393 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; + +/** + * The NioImageBuffer class is a container for an image whose DataBuffer + * is specified via a java.nio.Buffer. An an NioImageBuffer can be wrapped by + * an ImageComponent and used for texture mapping, or for rendering Raster + * objects or background images. An NioImageBuffer must not be used as the + * buffer of an off-screen Canvas3D, or for reading back a raster image. + * + * @see ImageComponent2D + * @see ImageComponent3D + * + * @since Java 3D 1.5 + */ +public class NioImageBuffer { + + /** + * Used to specify the type of the image. + */ + public enum ImageType { + /** + * Represents an image with 8-bit RGB color components, + * corresponding to a Windows-style BGR color model, with the + * colors Blue, Green, and Red stored in 3 consecutive + * bytes for each pixel. + * The data buffer must be a ByteBuffer when using this imageType. + */ + TYPE_3BYTE_BGR, + + /** + * Represents an image with 8-bit RGB color components with + * Red, Green, and Blue, stored in 3 consecutive + * bytes for each pixel. + * The data buffer must be a ByteBuffer when using this imageType. + */ + TYPE_3BYTE_RGB, + + /** + * Represents an image with 8-bit RGBA color components with + * Alpha, Blue, Green, and Red stored in 4 consecutive + * bytes for each pixel. + * The data buffer must be a ByteBuffer when using this imageType. + */ + TYPE_4BYTE_ABGR, + + /** + * Represents an image with 8-bit RGBA color components with + * Red, Green, Blue, and Alpha stored in 4 consecutive + * bytes for each pixel. + * The data buffer must be a ByteBuffer when using this imageType. + */ + TYPE_4BYTE_RGBA, + + /** + * Represents a unsigned byte grayscale image, non-indexed. + * The data buffer must be a ByteBuffer when using this imageType. + */ + TYPE_BYTE_GRAY, + + /** + * Represents an image with 8-bit RGBA color components packed + * into integer pixels. + * The data buffer must be an IntBuffer when using this imageType. + */ + TYPE_INT_ARGB, + + /** + * Represents an image with 8-bit RGB color components, + * corresponding to a Windows- or Solaris- style BGR color model, + * with the colors Blue, Green, and Red packed into integer + * pixels. + * The data buffer must be an IntBuffer when using this imageType. + */ + TYPE_INT_BGR, + + /** + * Represents an image with 8-bit RGB color components packed into + * integer pixels. + * The data buffer must be an IntBuffer when using this imageType. + */ + TYPE_INT_RGB, + + } + + + /** + * Enum for type of buffer + */ + enum BufferType { + BYTE_BUFFER, + INT_BUFFER, + } + + + // Width and height of image + int width; + int height; + + // TYpe of image + ImageType imageType; + + // Cached buffer + Buffer buffer; + + // Type of NIO Buffer: byte or int + BufferType bufferType; + + // Number of bytes allocated per pixel + int bytesPerPixel; + + // Number of byte or int elements per pixel + int elementsPerPixel; + + /** + * Constructs an NIO image buffer of the specified size and type. + * A direct NIO buffer of the correct type (ByteBuffer or IntBuffer) + * and size to match the input parameters + * is allocated. + * + * @param width width of the image + * @param height height of the image + * @param imageType type of the image. + * + * @exception IllegalArgumentException if width < 1 or height < 1 + * @exception NullPointerException if imageType is null + */ + public NioImageBuffer(int width, int height, ImageType imageType) { + + processParams(width, height, imageType); + + ByteBuffer tmpBuffer = ByteBuffer.allocateDirect(width * height * bytesPerPixel); + switch (bufferType) { + case BYTE_BUFFER: + buffer = tmpBuffer; + break; + + case INT_BUFFER: + buffer = tmpBuffer.order(ByteOrder.nativeOrder()).asIntBuffer(); + break; + + default: + // We should never get here + throw new AssertionError("missing case statement"); + } + } + + /** + * Constructs an NIO image buffer of the specified size and type, using + * the specified dataBuffer. + * The the byte order of the specified dataBuffer must match the native + * byte order of the underlying platform. + * For best performance, the NIO buffer should be a direct buffer. + * + * @param width width of the image + * @param height height of the image + * @param imageType type of the image. + * @param dataBuffer an NIO buffer of the correct type (ByteBuffer or + * IntBuffer) to match the specified imageType. + * This constructor will create a new view of + * the buffer, and will call rewind on that view, + * such that elements 0 through dataBuffer.limit()-1 + * will be available internally. The number of elements in + * the buffer must be exactly width*height*numElementsPerPixel, + * where numElementsPerPixel is + * 3 for TYPE_3BYTE_BGR and TYPE_3BYTE_RGB, + * 4 for TYPE_4BYTE_ABGR and TYPE_4BYTE_RGBA, + * and 1 for all other types. + * + * @exception IllegalArgumentException if width < 1 or height < 1 + * @exception NullPointerException if imageType or dataBuffer is null + * @exception IllegalArgumentException if the type of the dataBuffer does + * not match the imageType + * @exception IllegalArgumentException if dataBuffer.limit() != + * width*height*numElementsPerPixel + * @exception IllegalArgumentException if the byte order of the specified + * dataBuffer does not match the native byte order of the underlying + * platform. + */ + public NioImageBuffer(int width, int height, ImageType imageType, + Buffer dataBuffer) { + + processParams(width, height, imageType); + setDataBuffer(dataBuffer); + } + + /** + * Gets the width of this data buffer. + * + * @return the width of this data buffer. + */ + public int getWidth() { + return width; + } + + /** + * Gets the height of this data buffer. + * + * @return the width of this data buffer. + */ + public int getHeight() { + return height; + } + + /** + * Gets the image type of this data buffer. + * + * @return the image type of this data buffer. + */ + public ImageType getImageType() { + return imageType; + } + + /** + * Sets the data buffer to the specified input data buffer. + * The the byte order of the specified dataBuffer must match the native + * byte order of the underlying platform. + * For best performance, the NIO buffer should be a direct buffer. + * + * @param dataBuffer an NIO buffer of the correct type (ByteBuffer or + * IntBuffer) to match the imageType of this + * NioImageBuffer. This method will create a new view of + * the buffer, and will call rewind on that view, + * such that elements 0 through dataBuffer.limit()-1 + * will be available internally. The number of elements in + * the buffer must be exactly width*height*numElementsPerPixel, + * where numElementsPerPixel is + * 3 for TYPE_3BYTE_BGR and TYPE_3BYTE_RGB, + * 4 for TYPE_4BYTE_ABGR and TYPE_4BYTE_RGBA, + * and 1 for all other types. + * + * @exception NullPointerException if dataBuffer is null + * @exception IllegalArgumentException if the type of the dataBuffer does + * not match the imageType + * @exception IllegalArgumentException if dataBuffer.limit() != + * width*height*numElementsPerPixel + * @exception IllegalArgumentException if the byte order of the specified + * dataBuffer does not match the native byte order of the underlying + * platform. + */ + public void setDataBuffer(Buffer dataBuffer) { + if (dataBuffer == null) { + throw new NullPointerException(); + } + + if (dataBuffer.limit() != width*height*elementsPerPixel) { + throw new IllegalArgumentException(J3dI18N.getString("NioImageBuffer3")); + } + + switch (bufferType) { + case BYTE_BUFFER: + if (!(dataBuffer instanceof ByteBuffer)) { + throw new IllegalArgumentException(J3dI18N.getString("NioImageBuffer4")); + } + buffer = ((ByteBuffer)dataBuffer).duplicate().rewind(); + break; + + case INT_BUFFER: + if (!(dataBuffer instanceof IntBuffer)) { + throw new IllegalArgumentException(J3dI18N.getString("NioImageBuffer4")); + } + + if (((IntBuffer)dataBuffer).order() != ByteOrder.nativeOrder()) { + throw new IllegalArgumentException(J3dI18N.getString("NioImageBuffer5")); + } + buffer = ((IntBuffer)dataBuffer).duplicate().rewind(); + break; + + default: + // We should never get here + throw new AssertionError("missing case statement"); + } + } + + /** + * Gets the data buffer to the specified input data buffer. + * + * @return a view of the current data buffer for this NIO image buffer. + * This view will be rewound such that elements 0 + * through dataBuffer.limit()-1 are available. + */ + public Buffer getDataBuffer() { + Buffer tmpBuffer = null; + + switch (bufferType) { + case BYTE_BUFFER: + tmpBuffer = ((ByteBuffer)buffer).duplicate(); + break; + + case INT_BUFFER: + tmpBuffer = ((IntBuffer)buffer).duplicate(); + break; + + default: + // We should never get here + throw new AssertionError("missing case statement"); + } + + return tmpBuffer.rewind(); + } + + + // Sanity check the input parameters, calculate the buffer type and + // the number of bytes per pixel + private void processParams(int width, int height, ImageType imageType) { + if (width < 1) { + throw new IllegalArgumentException(J3dI18N.getString("NioImageBuffer0")); + } + + if (height < 1) { + throw new IllegalArgumentException(J3dI18N.getString("NioImageBuffer1")); + } + + switch (imageType) { + case TYPE_3BYTE_BGR: + bufferType = BufferType.BYTE_BUFFER; + bytesPerPixel = 3; + elementsPerPixel = 3; + break; + + case TYPE_3BYTE_RGB: + bufferType = BufferType.BYTE_BUFFER; + bytesPerPixel = 3; + elementsPerPixel = 3; + break; + + case TYPE_4BYTE_ABGR: + bufferType = BufferType.BYTE_BUFFER; + bytesPerPixel = 4; + elementsPerPixel = 4; + break; + + case TYPE_4BYTE_RGBA: + bufferType = BufferType.BYTE_BUFFER; + bytesPerPixel = 4; + elementsPerPixel = 4; + break; + + case TYPE_BYTE_GRAY: + bufferType = BufferType.BYTE_BUFFER; + bytesPerPixel = 1; + elementsPerPixel = 1; + break; + + case TYPE_INT_ARGB: + case TYPE_INT_BGR: + case TYPE_INT_RGB: + bufferType = BufferType.INT_BUFFER; + bytesPerPixel = 4; + elementsPerPixel = 1; + break; + + default: + // We should never get here + throw new AssertionError("missing case statement"); + } + + this.width = width; + this.height = height; + this.imageType = imageType; + } + +} diff --git a/src/javax/media/j3d/NnuId.java b/src/javax/media/j3d/NnuId.java new file mode 100644 index 0000000..cb959e1 --- /dev/null +++ b/src/javax/media/j3d/NnuId.java @@ -0,0 +1,39 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Defines a "not necessarily unique ID" + */ + +interface NnuId { + + abstract int equal(NnuId obj); + + abstract int getId(); + +} diff --git a/src/javax/media/j3d/NnuIdManager.java b/src/javax/media/j3d/NnuIdManager.java new file mode 100644 index 0000000..16fa62d --- /dev/null +++ b/src/javax/media/j3d/NnuIdManager.java @@ -0,0 +1,357 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; i0 && + (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 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 ((i < (nnuIdArr1.length - 1)) && nnuIdArr1[i].getId() == nnuIdArr1[i + 1].getId()) { + // Remove element from original array + NnuId[] tmpNnuIdArr0 = new NnuId[nnuIdArr0.length - 1]; + System.arraycopy(nnuIdArr0, 0, tmpNnuIdArr0, 0, index); + System.arraycopy(nnuIdArr0, index + 1, + tmpNnuIdArr0, index, nnuIdArr0.length - index - 1); + nnuIdArr0 = tmpNnuIdArr0; + } else { + // Copy elements from original array to new array up to + // but not including the element we are removing + if (index == curStart) { + curStart++; + } else { + len = index - curStart; + System.arraycopy(nnuIdArr0, curStart, + newNnuIdArr, newStart, len); + + curStart = index + 1; + newStart = newStart + len; + } + } + } else { + found = false; + MasterControl.getCoreLogger().severe("Can't Find matching nnuId."); + } + } + + 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 !!! + MasterControl.getCoreLogger().severe("Attempt to remove more elements than are present"); + } + + 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= 0) { + blkSize = lenLess1 - index[0]; + System.arraycopy(nnuIdArr, index[0]+1, + nnuIdArr, index[0], blkSize); + nnuIdArr[lenLess1] = null; + } + else { + MasterControl.getCoreLogger().severe("Can't Find matching nnuId."); + } + + // 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 + * For more information, see the + * Introduction to the Java 3D API. + * + *

+ * NOTE: Applications should not 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; + + /** + * Specifies that this Node allows read access to its parent Group node. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_PARENT_READ = CapabilityBits.NODE_ALLOW_PARENT_READ; + + /** + * Specifies that this Node allows read access to its Locale. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_LOCALE_READ = CapabilityBits.NODE_ALLOW_LOCALE_READ; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_BOUNDS_READ, + ALLOW_PICKABLE_READ, + ALLOW_COLLIDABLE_READ, + ALLOW_AUTO_COMPUTE_BOUNDS_READ, + ALLOW_LOCAL_TO_VWORLD_READ, + ALLOW_PARENT_READ, + ALLOW_LOCALE_READ + }; + + // for checking for cycles + private boolean visited = false; + + + /** + * Constructs a Node object with default parameters. The default + * values are as follows: + *

    + * pickable : true
    + * collidable : true
    + * bounds auto compute : true
    + * bounds : N/A (automatically computed)
    + *
+ */ + public Node() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + + * @return the parent of this node, or null if this node has no parent + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public Node getParent() { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_PARENT_READ)) { + throw new CapabilityNotSetException(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 + * this node. It is only valid + * for nodes that are part of a live scene graph. + * If the node is not part of a live scene graph then the coordinates are + * calculated as if the graph was attached at the origin of a locale. + * @param t the object that will receive the local coordinates to + * Vworld coordinates transform. + * @exception RestrictedAccessException if the node is compiled but not + * part of a live scene graph + * @exception CapabilityNotSetException if appropriate capability is + * not set and this node is part of live or compiled scene graph + * @exception IllegalSharingException if the node is a descendant + * of a SharedGroup node. + */ + public void getLocalToVworld(Transform3D t) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("Node8")); + } + + if (!isLive()) { + // TODO Support compiled graphs + if (isCompiled()) + throw new RestrictedAccessException(J3dI18N.getString("Node7")); + + // In 1.4 we support getLocalToVworld for non live nodes + ((NodeRetained)this.retained).computeNonLiveLocalToVworld(t, this); + //throw new RestrictedAccessException(J3dI18N.getString("Node7")); + } else { + ((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 + * this 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 not + * 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 + * @exception IllegalSharingException if the node is not a descendant + * of a SharedGroup 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); + + } + + /** + * Retrieves the locale to which this node is attached. If the + * node is not part of a live scene graph, null is returned. + * + * @return the locale to which this node is attached. + * + * @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. + * + * @since Java 3D 1.4 + */ + public Locale getLocale() { + if (!isLive()) { + return null; + } + + if(!this.getCapability(ALLOW_LOCALE_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("Node17")); + } + + return ((NodeRetained)this.retained).getLocale(); + } + + /** + * Duplicates all the nodes of the specified sub-graph. For Group Nodes + * the group node is duplicated via a call to cloneNode + * and then cloneTree + * 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate 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 cloneNode + * and then cloneTree 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate paramter. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 cloneNode and + * then cloneTree 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate paramter. + * + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree + * flag to be ignored. When false, the value of each node's + * duplicateOnCloneTree determines whether data is + * duplicated or copied. + * + * @param allowDanglingReferences when set to true allows + * the cloneTree + * method to complete even whan a dangling reference is discovered. When + * this parameter is false a + * DanglingReferenceException 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 + * allowDanglingReference parameter is false. + * @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 cloneNode + * and then cloneTree + * 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate 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 cloneNode + * and then cloneTree 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 cloneNode + * and then cloneTree 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree determines whether data is + * duplicated or copied. + * + * @param allowDanglingReferences when set to true allows + * the cloneTree + * method to complete even whan a dangling reference is discovered. When + * this parameter is false a + * DanglingReferenceException 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 cloneNode and + * then cloneTree 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 + * duplicateOnCloneTree flag found in every Leaf Node's + * component data class and by the forceDuplicate paramter. + * + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree + * flag to be ignored. When false, the value of each node's + * duplicateOnCloneTree 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 cloneTree to duplicate the current node. + * cloneNode should be overridden by any user subclassed + * objects. All subclasses must have their cloneNode + * method consist of the following lines: + *

+     *     public Node cloneNode(boolean forceDuplicate) {
+     *         UserSubClass usc = new UserSubClass();
+     *         usc.duplicateNode(this, forceDuplicate);
+     *         return usc;
+     *     }
+     * 
+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + * + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 originalNode into + * the current node. This method is called from subclass of + * duplicateNode method which is, in turn, called by the + * cloneNode method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + * + * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 true this Node 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 Node 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/javax/media/j3d/NodeComponent.java b/src/javax/media/j3d/NodeComponent.java new file mode 100644 index 0000000..fb0d0b5 --- /dev/null +++ b/src/javax/media/j3d/NodeComponent.java @@ -0,0 +1,308 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * For more information, see the + * Introduction to the Java 3D API. + */ +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: + *

    + * duplicate on clone tree : false
    + *
+ */ + public NodeComponent() { + } + + /** + * Sets this node's duplicateOnCloneTree value. The + * duplicateOnCloneTree value is used to determine if NodeComponent + * objects are to be duplicated or referenced during a + * cloneTree operation. A value of true means + * that this NodeComponent object should be duplicated, while a value + * of false indicates that this NodeComponent object's + * reference will be copied into the newly cloned object. This value + * can be overriden via the forceDuplicate parameter of + * the cloneTree 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 + * duplicateOnCloneTree value is used to determine if NodeComponent + * objects are to be duplicated or referenced during a + * cloneTree operation. A value of true means + * that this NodeComponent object should be duplicated, while a value + * of false indicates that this NodeComponent object's + * reference will be copied into the newly cloned object. This value + * can be overriden via the forceDuplicate parameter of + * the cloneTree 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 + * cloneNodeComponent(boolean forceDuplicate) + */ + public NodeComponent cloneNodeComponent() { + throw new RuntimeException(J3dI18N.getString("NodeComponent0")); + } + + + /** + * NOTE: Applications should not call this method directly. + * It should only be called by the cloneNode method. + * + * @deprecated As of Java 3D version 1.2, replaced by + * duplicateNodeComponent(NodeComponent + * originalNodeComponent, boolean forceDuplicate) + */ + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + duplicateAttributes(originalNodeComponent, + originalNodeComponent.forceDuplicate); + } + + /** + * Copies all node information from originalNodeComponent into + * the current node component. This method is called from subclass of + * duplicateNodeComponent method which is, in turn, called by the + * cloneNodeComponent method. + * + * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + * + * @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 originalNodeComponent + * into the current node. This method is called from the + * cloneNodeComponent method which is, in turn, called + * by the cloneNode method. + *
+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneNode method. + * + * @param originalNodeComponent the node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 cloneNode to duplicate the + * current node.
+ * + * cloneNodeComponent should be overridden by any user + * subclassed NodeComponent objects. All subclasses must have their + * cloneNodeComponent + * method consist of the following lines: + *

+   *     public NodeComponent cloneNodeComponent(boolean forceDuplicate) {
+   *         UserNodeComponent unc = new UserNodeComponent();
+   *         unc.duplicateNodeComponent(this, forceDuplicate);
+   *         return unc;
+   *     }
+   * 
+ * + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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. + */ + @Override + 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(); + } + + /* + * @exception IllegalSharingException if this NodeComponent is live and + * the specified image is being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this NodeComponent is + * being used by an immediate mode context and + * the specified image is being used by a Canvas3D as an off-screen buffer. + */ + void validateImageIllegalSharing(ImageComponent image) { + // Do illegal sharing check + if(image != null) { + ImageComponentRetained imageRetained = (ImageComponentRetained) image.retained; + NodeComponentRetained ncRetained = (NodeComponentRetained)this.retained; + if(imageRetained.getUsedByOffScreen()) { + if(isLive()) { + throw new IllegalSharingException(J3dI18N.getString("NodeComponent2")); + } + if(ncRetained.getInImmCtx()) { + throw new IllegalSharingException(J3dI18N.getString("NodeComponent3")); + } + } + } + } + +} diff --git a/src/javax/media/j3d/NodeComponentRetained.java b/src/javax/media/j3d/NodeComponentRetained.java new file mode 100644 index 0000000..c424257 --- /dev/null +++ b/src/javax/media/j3d/NodeComponentRetained.java @@ -0,0 +1,248 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + private 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=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 + * duplicateOnCloneTree value is used to determine if NodeComponent + * objects are to be duplicated or referenced during a + * cloneTree operation. A value of true means + * that this NodeComponent object should be duplicated, while a value + * of false indicates that this NodeComponent object's + * reference will be copied into the newly cloned object. This value + * can be overriden via the forceDuplicate parameter of + * the cloneTree method. + * @param duplicate the value to set. + * @see Node#cloneTree + */ + void setDuplicateOnCloneTree(boolean duplicate) { + duplicateOnCloneTree = duplicate; + } + + /** + * Returns this node's duplicateOnCloneTree value. The + * duplicateOnCloneTree value is used to determine if NodeComponent + * objects are to be duplicated or referenced during a + * cloneTree operation. A value of true means + * that this NodeComponent object should be duplicated, while a value + * of false indicates that this NodeComponent object's + * reference will be copied into the newly cloned object. This value + * can be overriden via the forceDuplicate parameter of + * the cloneTree 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; + } + + void setFrequencyChangeMask(int bit, int mask) { + if (source.getCapabilityIsFrequent(bit)) + changedFrequent |= mask; + else if (!source.isLive()) { + // Record the freq->infreq change only for non-live node components + changedFrequent &= ~mask; + } + } + + @Override + 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/javax/media/j3d/NodeComponentUpdate.java b/src/javax/media/j3d/NodeComponentUpdate.java new file mode 100644 index 0000000..677e36f --- /dev/null +++ b/src/javax/media/j3d/NodeComponentUpdate.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/NodeData.java b/src/javax/media/j3d/NodeData.java new file mode 100644 index 0000000..ad822fd --- /dev/null +++ b/src/javax/media/j3d/NodeData.java @@ -0,0 +1,35 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +class NodeData { + // per path node data + // XXXX: replace per path mirror objects with node data + // XXXX: move other basic node's data here + SwitchState switchState = null; +} diff --git a/src/javax/media/j3d/NodeReferenceTable.java b/src/javax/media/j3d/NodeReferenceTable.java new file mode 100644 index 0000000..1bab869 --- /dev/null +++ b/src/javax/media/j3d/NodeReferenceTable.java @@ -0,0 +1,135 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Hashtable; + +/** + * The NodeReferenceTable object is used by a leaf node's + * updateNodeReferences method called by the + * cloneTree 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. + *

+ * During a cloneTree call, after all nodes have been duplicated, + * each SceneGraphObject's updateNodeReferences method is called. + * This method takes a NodeReferenceTable object as a parameter. The + * SceneGraphObject's updateNodeReferences method can then use the + * getNewObjectReference 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 cloneTree + * method or a reference to the original + * SceneGraphObject is returned depending on the value of the + * allowDanglingReferences parameter passed in the + * cloneTree 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 cloneTree + * method. It can be used by the updateNodeReferences + * method to see if a SceneGraphObject that is being referenced has been duplicated + * in the new cloneTree sub-graph. + *

+ * A SceneGraphObject's updateNodeReferences 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 + * allowDanglingReferences parameter passed in the + * cloneTree 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 + * cloneTree method or a reference to the original object + * is returned depending on the value of the + * allowDanglingReferences parameter passed in the + * cloneTree 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/javax/media/j3d/NodeRetained.java b/src/javax/media/j3d/NodeRetained.java new file mode 100644 index 0000000..83739da --- /dev/null +++ b/src/javax/media/j3d/NodeRetained.java @@ -0,0 +1,983 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +import java.util.ArrayList; +import java.util.Vector; + +/** + * 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; + + protected Bounds cachedBounds=null; // Cached auto compute bounds, could we use localBounds ? + protected boolean validCachedBounds = false; // Fix to Issue 514 + /** + * 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((Bounds)null); + } + + + @Override + public int getId() { + return nnuId; + } + + @Override + 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) { + if (this.boundsAutoCompute==autoCompute) { + return; + } + + this.boundsAutoCompute = autoCompute; + dirtyBoundsCache(); + } + + /** + * 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 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= 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() {} + + + @Override + void setLive(SetLiveState s) { + int oldrefCount = refCount; + + doSetLive(s); + if (oldrefCount <= 0) + super.markAsLive(); + } + + // The default set of setLive actions. + @Override + 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.err.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 = parents.get(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 = parents.get(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 = parents.get(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); + } + } + } + + + @Override + boolean isStatic() { + if (source.getCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ) || + source.getCapability(Node.ALLOW_PARENT_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; + } + + @Override + void merge(CompileState compState) { + staticTransform = compState.staticTransform; + if (compState.parentGroup != null) { + compState.parentGroup.compiledChildrenList.add(this); + } + parent = compState.parentGroup; + if (staticTransform != null) { + mergeTransform(staticTransform); + } + } + + @Override + 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; + + } + + @Override + VirtualUniverse getVirtualUniverse() { + return universe; + } + + void searchGeometryAtoms(UnorderList list) {} + + /** + * Make the boundsCache of this node and all its parents dirty + */ + void dirtyBoundsCache() { + // Possible optimisation is to not traverse up the tree + // if the cachedBounds==null. However this is not the case + // if the node is the child of a SharedGroup + if (VirtualUniverse.mc.cacheAutoComputedBounds) { + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + validCachedBounds = false; + if (parent!=null) { + parent.dirtyBoundsCache(); + } + } + } +} + diff --git a/src/javax/media/j3d/NoopDrawingSurfaceObject.java b/src/javax/media/j3d/NoopDrawingSurfaceObject.java new file mode 100644 index 0000000..0c41322 --- /dev/null +++ b/src/javax/media/j3d/NoopDrawingSurfaceObject.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The DrawingSurfaceObject class is used to manage native drawing surface + */ +class NoopDrawingSurfaceObject extends DrawingSurfaceObject { + + NoopDrawingSurfaceObject(Canvas3D cv) { + super(cv); + + System.err.println("NoopDrawingSurfaceObject constructed"); + } + + @Override + synchronized boolean renderLock() { +// System.err.println("NoopDrawingSurfaceObject.renderLock()"); + gotDsiLock = true; + return true; + } + + @Override + synchronized void unLock() { +// System.err.println("NoopDrawingSurfaceObject.unLock()"); + gotDsiLock = false; + } + + @Override + synchronized void getDrawingSurfaceObjectInfo() { + if (canvas.drawable == null) { + System.err.println( + "NoopDrawingSurfaceObject.getDrawingSurfaceObjectInfo: window = " + + canvas.drawable); + + canvas.drawable = new NoopDrawable(); + } + } + + @Override + synchronized void invalidate() { + System.err.println("NoopDrawingSurfaceObject.invalidate()"); + } + + /** + * Dummy drawable for noop pipeline + */ + static class NoopDrawable implements Drawable { + } + +} diff --git a/src/javax/media/j3d/NoopPipeline.java b/src/javax/media/j3d/NoopPipeline.java new file mode 100644 index 0000000..64c0ccd --- /dev/null +++ b/src/javax/media/j3d/NoopPipeline.java @@ -0,0 +1,1448 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.nio.Buffer; +import java.nio.FloatBuffer; + +/** + * Concrete implementation of Pipeline class for the noop rendering + * pipeline. + */ +class NoopPipeline extends Pipeline { + /** + * Constructor for singleton NoopPipeline instance + */ + protected NoopPipeline() { + } + + /** + * Initialize the pipeline + */ + @Override + void initialize(Pipeline.Type pipelineType) { + super.initialize(pipelineType); + + assert pipelineType == Pipeline.Type.NOOP; + } + + // --------------------------------------------------------------------- + + // + // GeometryArrayRetained methods + // + + // used for GeometryArrays by Copy or interleaved + @Override + void execute(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int startVIndex, int vcount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + int vertexAttrCount, int[] vertexAttrSizes, + float[] varray, float[] cdata, int cdirty) { + } + + // used by GeometryArray by Reference with java arrays + @Override + void executeVA(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + 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 vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndex, float[][] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int[] texIndex, int texstride, Object[] texCoords, + int cdirty) { + } + + // used by GeometryArray by Reference with NIO buffer + @Override + void executeVABuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int vcount, + int vformat, + int vdefined, + int coordIndex, + Buffer vcoords, + int colorIndex, + Buffer cdataBuffer, + float[] cfdata, byte[] cbdata, + int normalIndex, FloatBuffer ndata, + int vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndex, FloatBuffer[] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int[] texIndex, int texstride, Object[] texCoords, + int cdirty) { + } + + // used by GeometryArray by Reference in interleaved format with NIO buffer + @Override + void executeInterleavedBuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int startVIndex, int vcount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + FloatBuffer varray, float[] cdata, int cdirty) { + } + + @Override + void setVertexFormat(Context ctx, GeometryArrayRetained geo, + int vformat, boolean useAlpha, boolean ignoreVertexColors) { + } + + // used for GeometryArrays + @Override + void buildGA(Context 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, + int vertexAttrCount, int[] vertexAttrSizes, + double[] xform, double[] nxform, + float[] varray) { + } + + // used to Build Dlist GeometryArray by Reference with java arrays + @Override + void buildGAForByRef(Context 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 vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndex, float[][] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int[] texIndex, int texstride, Object[] texCoords, + double[] xform, double[] nxform) { + } + + + // --------------------------------------------------------------------- + + // + // IndexedGeometryArrayRetained methods + // + + // by-copy or interleaved, by reference, Java arrays + @Override + void executeIndexedGeometry(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + float[] varray, float[] cdata, + int cdirty, + int[] indexCoord) { + } + + // interleaved, by reference, nio buffer + @Override + void executeIndexedGeometryBuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + FloatBuffer varray, float[] cdata, + int cdirty, + int[] indexCoord) { + } + + // non interleaved, by reference, Java arrays + @Override + void executeIndexedGeometryVA(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vdefined, + float[] vfcoords, double[] vdcoords, + float[] cfdata, byte[] cbdata, + float[] ndata, + int vertexAttrCount, int[] vertexAttrSizes, + float[][] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int texstride, Object[] texCoords, + int cdirty, + int[] indexCoord) { + } + + // non interleaved, by reference, nio buffer + @Override + void executeIndexedGeometryVABuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vdefined, + Buffer vcoords, + Buffer cdataBuffer, + float[] cfdata, byte[] cbdata, + FloatBuffer normal, + int vertexAttrCount, int[] vertexAttrSizes, + FloatBuffer[] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int texstride, Object[] texCoords, + int cdirty, + int[] indexCoord) { + } + + // by-copy geometry + @Override + void buildIndexedGeometry(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, boolean updateAlpha, + float alpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetMapOffset, + double[] xform, double[] nxform, + float[] varray, int[] indexCoord) { + } + + + // --------------------------------------------------------------------- + + // + // GraphicsContext3D methods + // + + @Override + void readRaster(Context ctx, + int type, int xSrcOffset, int ySrcOffset, + int width, int height, int hCanvas, + int imageDataType, int imageFormat, + Object imageBuffer, + int depthFormat, + Object depthBuffer) { + + } + + // --------------------------------------------------------------------- + + // + // GLSLShaderProgramRetained methods + // + + // ShaderAttributeValue methods + + @Override + ShaderError setGLSLUniform1i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int value) { + return null; + } + + @Override + ShaderError setGLSLUniform1f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float value) { + return null; + } + + @Override + ShaderError setGLSLUniform2i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform2f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform3i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform4i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniformMatrix3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniformMatrix4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value) { + return null; + } + + // ShaderAttributeArray methods + + @Override + ShaderError setGLSLUniform1iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform1fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform2iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform2fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform3iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform4iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value) { + return null; + } + + @Override + ShaderError setGLSLUniform4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniformMatrix3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + return null; + } + + @Override + ShaderError setGLSLUniformMatrix4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value) { + return null; + } + + // interfaces for shader compilation, etc. + @Override + ShaderError createGLSLShader(Context ctx, int shaderType, ShaderId[] shaderId) { + return null; + } + @Override + ShaderError destroyGLSLShader(Context ctx, ShaderId shaderId) { + return null; + } + @Override + ShaderError compileGLSLShader(Context ctx, ShaderId shaderId, String program) { + return null; + } + + @Override + ShaderError createGLSLShaderProgram(Context ctx, ShaderProgramId[] shaderProgramId) { + return null; + } + @Override + ShaderError destroyGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId) { + return null; + } + @Override + ShaderError linkGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId, + ShaderId[] shaderIds) { + return null; + } + @Override + ShaderError bindGLSLVertexAttrName(Context ctx, ShaderProgramId shaderProgramId, + String attrName, int attrIndex) { + return null; + } + @Override + void lookupGLSLShaderAttrNames(Context ctx, ShaderProgramId shaderProgramId, + int numAttrNames, String[] attrNames, ShaderAttrLoc[] locArr, + int[] typeArr, int[] sizeArr, boolean[] isArrayArr) { + } + + @Override + ShaderError useGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId) { + return null; + } + + + // --------------------------------------------------------------------- + + // + // ColoringAttributesRetained methods + // + + @Override + void updateColoringAttributes(Context ctx, + float dRed, float dGreen, float dBlue, + float red, float green, float blue, + float alpha, + boolean lEnable, + int shadeModel) { + } + + + // --------------------------------------------------------------------- + + // + // DirectionalLightRetained methods + // + + @Override + void updateDirectionalLight(Context ctx, + int lightSlot, float red, float green, + float blue, float x, float y, float z) { + } + + + // --------------------------------------------------------------------- + + // + // PointLightRetained methods + // + + @Override + void updatePointLight(Context ctx, + int lightSlot, float red, float green, + float blue, float ax, float ay, float az, + float px, float py, float pz) { + } + + + // --------------------------------------------------------------------- + + // + // SpotLightRetained methods + // + + @Override + void updateSpotLight(Context 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) { + } + + + // --------------------------------------------------------------------- + + // + // ExponentialFogRetained methods + // + + @Override + void updateExponentialFog(Context ctx, + float red, float green, float blue, + float density) { + } + + + // --------------------------------------------------------------------- + + // + // LinearFogRetained methods + // + + @Override + void updateLinearFog(Context ctx, + float red, float green, float blue, + double fdist, double bdist) { + } + + + // --------------------------------------------------------------------- + + // + // LineAttributesRetained methods + // + + @Override + void updateLineAttributes(Context ctx, + float lineWidth, int linePattern, + int linePatternMask, + int linePatternScaleFactor, + boolean lineAntialiasing) { + } + + + // --------------------------------------------------------------------- + + // + // MaterialRetained methods + // + + @Override + void updateMaterial(Context 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) { + } + + + // --------------------------------------------------------------------- + + // + // ModelClipRetained methods + // + + @Override + void updateModelClip(Context ctx, int planeNum, boolean enableFlag, + double A, double B, double C, double D) { + } + + + // --------------------------------------------------------------------- + + // + // PointAttributesRetained methods + // + + @Override + void updatePointAttributes(Context ctx, float pointSize, boolean pointAntialiasing) { + } + + + // --------------------------------------------------------------------- + + // + // PolygonAttributesRetained methods + // + + @Override + void updatePolygonAttributes(Context ctx, + int polygonMode, int cullFace, + boolean backFaceNormalFlip, + float polygonOffset, + float polygonOffsetFactor) { + } + + + // --------------------------------------------------------------------- + + // + // RenderingAttributesRetained methods + // + + @Override + void updateRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride, + boolean depthBufferEnable, + boolean depthBufferWriteEnable, + int depthTestFunction, + float alphaTestValue, int alphaTestFunction, + boolean ignoreVertexColors, + boolean rasterOpEnable, int rasterOp, + boolean userStencilAvailable, boolean stencilEnable, + int stencilFailOp, int stencilZFailOp, int stencilZPassOp, + int stencilFunction, int stencilReferenceValue, + int stencilCompareMask, int stencilWriteMask ) { + } + + + // --------------------------------------------------------------------- + + // + // TexCoordGenerationRetained methods + // + + /** + * This method updates the native context: + * trans contains eyeTovworld transform in d3d + * trans contains vworldToEye transform in ogl + */ + @Override + void updateTexCoordGeneration(Context 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) { + } + + + // --------------------------------------------------------------------- + + // + // TransparencyAttributesRetained methods + // + + @Override + void updateTransparencyAttributes(Context ctx, + float alpha, int geometryType, + int polygonMode, + boolean lineAA, boolean pointAA, + int transparencyMode, + int srcBlendFunction, + int dstBlendFunction) { + } + + + // --------------------------------------------------------------------- + + // + // TextureAttributesRetained methods + // + + @Override + void updateTextureAttributes(Context ctx, + double[] transform, boolean isIdentity, int textureMode, + int perspCorrectionMode, float red, + float green, float blue, float alpha, + int textureFormat) { + } + + @Override + void updateRegisterCombiners(Context 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) { + } + + @Override + void updateTextureColorTable(Context ctx, int numComponents, + int colorTableSize, + int[] colorTable) { + } + + @Override + void updateCombiner(Context ctx, + int combineRgbMode, int combineAlphaMode, + int[] combineRgbSrc, int[] combineAlphaSrc, + int[] combineRgbFcn, int[] combineAlphaFcn, + int combineRgbScale, int combineAlphaScale) { + } + + + // --------------------------------------------------------------------- + + // + // TextureUnitStateRetained methods + // + + @Override + void updateTextureUnitState(Context ctx, int unitIndex, boolean enableFlag) { + } + + + // --------------------------------------------------------------------- + + // + // TextureRetained methods + // Texture2DRetained methods + // + + @Override + void bindTexture2D(Context ctx, int objectId, boolean enable) { + } + + @Override + void updateTexture2DImage(Context ctx, + int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int imageDataType, Object data, boolean useAutoMipMap) { + } + + @Override + void updateTexture2DSubImage(Context ctx, + int level, int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object data, boolean useAutoMipMap) { + } + + @Override + void updateTexture2DLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + } + + @Override + void updateTexture2DLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + } + + @Override + void updateTexture2DBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + } + + @Override + void updateTexture2DFilterModes(Context ctx, + int minFilter, int magFilter) { + } + + @Override + void updateTexture2DSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + } + + @Override + void updateTexture2DFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + } + + @Override + void updateTexture2DAnisotropicFilter(Context ctx, float degree) { + } + + + // --------------------------------------------------------------------- + + // + // Texture3DRetained methods + // + + @Override + void bindTexture3D(Context ctx, int objectId, boolean enable) { + } + + @Override + void updateTexture3DImage(Context ctx, + int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, int depth, + int boundaryWidth, + int imageDataType, Object imageData, boolean useAutoMipMap) { + } + + @Override + void updateTexture3DSubImage(Context ctx, + int level, + int xoffset, int yoffset, int zoffset, + int textureFormat, int imageFormat, + int imgXoffset, int imgYoffset, int imgZoffset, + int tilew, int tileh, + int width, int height, int depth, + int imageTypeData, Object imageData, boolean useAutoMipMap) { + } + + @Override + void updateTexture3DLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + } + + @Override + void updateTexture3DLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + } + + @Override + void updateTexture3DBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + int boundaryModeR, float boundaryRed, + float boundaryGreen, float boundaryBlue, + float boundaryAlpha) { + } + + @Override + void updateTexture3DFilterModes(Context ctx, + int minFilter, int magFilter) { + } + + @Override + void updateTexture3DSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + } + + @Override + void updateTexture3DFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + } + + @Override + void updateTexture3DAnisotropicFilter(Context ctx, float degree) { + } + + + // --------------------------------------------------------------------- + + // + // TextureCubeMapRetained methods + // + + @Override + void bindTextureCubeMap(Context ctx, int objectId, boolean enable) { + } + + @Override + void updateTextureCubeMapImage(Context ctx, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int imageDataType, Object imageData, boolean useAutoMipMap) { + } + + @Override + void updateTextureCubeMapSubImage(Context ctx, + int face, int level, int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object imageData, boolean useAutoMipMap) { + } + + @Override + void updateTextureCubeMapLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + } + + @Override + void updateTextureCubeMapLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + } + + @Override + void updateTextureCubeMapBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + } + + @Override + void updateTextureCubeMapFilterModes(Context ctx, + int minFilter, int magFilter) { + } + + @Override + void updateTextureCubeMapSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + } + + @Override + void updateTextureCubeMapFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + } + + @Override + void updateTextureCubeMapAnisotropicFilter(Context ctx, float degree) { + } + + // --------------------------------------------------------------------- + + // + // MasterControl methods + // + + // Maximum lights supported by the native API + @Override + int getMaximumLights() { + return 8; + } + + + // --------------------------------------------------------------------- + + // + // Canvas3D methods - native wrappers + // + + // This is the native method for creating the underlying graphics context. + @Override + Context createNewContext(Canvas3D cv, Drawable drawable, + Context shareCtx, boolean isSharedCtx, + boolean offScreen) { + return new NoopContext(); + } + + @Override + void createQueryContext(Canvas3D cv, Drawable drawable, + boolean offScreen, int width, int height) { + } + + // This is the native for creating offscreen buffer + @Override + Drawable createOffScreenBuffer(Canvas3D cv, Context ctx, int width, int height) { + return null; + } + + @Override + void destroyOffScreenBuffer(Canvas3D cv, Context ctx, Drawable drawable) { + } + + // This is the native for reading the image from the offscreen buffer + @Override + void readOffScreenBuffer(Canvas3D cv, Context ctx, int format, int type, Object data, int width, int height) { + } + +// The native method for swapBuffers +@Override +void swapBuffers(Canvas3D cv, Context ctx, Drawable drawable) {} + + // native method for setting Material when no material is present + @Override + void updateMaterialColor(Context ctx, float r, float g, float b, float a) { + } + + @Override + void destroyContext(Drawable drawable, Context ctx) { + } + + // This is the native method for doing accumulation. + @Override + void accum(Context ctx, float value) { + } + + // This is the native method for doing accumulation return. + @Override + void accumReturn(Context ctx) { + } + + // This is the native method for clearing the accumulation buffer. + @Override + void clearAccum(Context ctx) { + } + + // This is the native method for getting the number of lights the underlying + // native library can support. + @Override + int getNumCtxLights(Context ctx) { + return 0; + } + + // Native method for decal 1st child setup + @Override + boolean decal1stChildSetup(Context ctx) { + return false; + } + + // Native method for decal nth child setup + @Override + void decalNthChildSetup(Context ctx) { + } + + // Native method for decal reset + @Override + void decalReset(Context ctx, boolean depthBufferEnable) { + } + + // Native method for decal reset + @Override + void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable) { + } + + // The following three methods are used in multi-pass case + + // native method for setting blend color + @Override + void setBlendColor(Context ctx, float red, float green, + float blue, float alpha) { + } + + // native method for setting blend func + @Override + void setBlendFunc(Context ctx, int src, int dst) { + } + + // native method for setting fog enable flag + @Override + void setFogEnableFlag(Context ctx, boolean enableFlag) { + } + + // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported + @Override + void setFullSceneAntialiasing(Context ctx, boolean enable) { + } + + // Native method to update separate specular color control + @Override + void updateSeparateSpecularColorEnable(Context ctx, boolean control) { + } + + // True under Solaris, + // False under windows when display mode <= 8 bit + @Override + boolean validGraphicsMode() { + return true; + } + + // native method for setting light enables + @Override + void setLightEnables(Context ctx, long enableMask, int maxLights) { + } + + // native method for setting scene ambient + @Override + void setSceneAmbient(Context ctx, float red, float green, float blue) { + } + + // native method for disabling fog + @Override + void disableFog(Context ctx) { + } + + // native method for disabling modelClip + @Override + void disableModelClip(Context ctx) { + } + + // native method for setting default RenderingAttributes + @Override + void resetRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride) { + } + + // native method for setting default texture + @Override + void resetTextureNative(Context ctx, int texUnitIndex) { + } + + // native method for activating a particular texture unit + @Override + void activeTextureUnit(Context ctx, int texUnitIndex) { + } + + // native method for setting default TexCoordGeneration + @Override + void resetTexCoordGeneration(Context ctx) { + } + + // native method for setting default TextureAttributes + @Override + void resetTextureAttributes(Context ctx) { + } + + // native method for setting default PolygonAttributes + @Override + void resetPolygonAttributes(Context ctx) { + } + + // native method for setting default LineAttributes + @Override + void resetLineAttributes(Context ctx) { + } + + // native method for setting default PointAttributes + @Override + void resetPointAttributes(Context ctx) { + } + + // native method for setting default TransparencyAttributes + @Override + void resetTransparency(Context ctx, int geometryType, + int polygonMode, boolean lineAA, + boolean pointAA) { + } + + // native method for setting default ColoringAttributes + @Override + void resetColoringAttributes(Context ctx, + float r, float g, + float b, float a, + boolean enableLight) { + } + + /** + * This native method makes sure that the rendering for this canvas + * gets done now. + */ + @Override + void syncRender(Context ctx, boolean wait) { + } + + // The native method that sets this ctx to be the current one + @Override + boolean useCtx(Context ctx, Drawable drawable) { + return true; + } + + @Override + void clear(Context ctx, float r, float g, float b, boolean clearStencil) { + + } + + @Override + void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, boolean useBiliearFilter) { + + } + + @Override + void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, float mapZ, float alpha, + boolean useBiliearFilter) { + + } + + @Override + void executeRasterDepth(Context ctx, float posX, float posY, float posZ, + int srcOffsetX, int srcOffsetY, int rasterWidth, int rasterHeight, + int depthWidth, int depthHeight, int depthType, Object depthData) { + + } + + // The native method for setting the ModelView matrix. + @Override + void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix) { + } + + // The native method for setting the Projection matrix. + @Override + void setProjectionMatrix(Context ctx, double[] projMatrix) { + } + + @Override + void resizeOffscreenLayer(Canvas3D cv, int width, int height) {} + + // The native method for setting the Viewport. + @Override + void setViewport(Context ctx, int x, int y, int width, int height) { + } + + // used for display Lists + @Override + void newDisplayList(Context ctx, int displayListId) { + } + @Override + void endDisplayList(Context ctx) { + } + @Override + void callDisplayList(Context ctx, int id, boolean isNonUniformScale) { + } + + @Override + void freeDisplayList(Context ctx, int id) { + } + @Override + void freeTexture(Context ctx, int id) { + } + + @Override + int generateTexID(Context ctx) { + return 0; + } + + @Override + void texturemapping(Context 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) { + } + + @Override + boolean initTexturemapping(Context ctx, int texWidth, + int texHeight, int objectId) { + return true; + } + + + // 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. + @Override + void setRenderMode(Context ctx, int mode, boolean doubleBuffer) { + } + + // Set glDepthMask. + @Override + void setDepthBufferWriteEnable(Context ctx, boolean mode) { + } + + + // --------------------------------------------------------------------- + + // + // Canvas3D / GraphicsConfigTemplate3D methods - logic dealing with + // native graphics configuration or drawing surface + // + + // Return a graphics config based on the one passed in. Note that we can + // assert that the input config is non-null and was created from a + // GraphicsConfigTemplate3D. + // This method must return a valid GraphicsConfig, or else it must throw + // an exception if one cannot be returned. + @Override + GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig) { + System.err.println("NoopPipeline.getGraphicsConfig()"); + return gconfig; + } + + // Get best graphics config from pipeline + @Override + GraphicsConfiguration getBestConfiguration(GraphicsConfigTemplate3D gct, + GraphicsConfiguration[] gc) { + + GraphicsConfiguration gc1 = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + // We need to cache the GraphicsTemplate3D + synchronized (Canvas3D.graphicsConfigTable) { + if (Canvas3D.graphicsConfigTable.get(gc1) == null) { + GraphicsConfigInfo gcInfo = new GraphicsConfigInfo(gct); +// gcInfo.setPrivateData(privateData); + Canvas3D.graphicsConfigTable.put(gc1, gcInfo); + } + } + return gc1; + } + + // Determine whether specified graphics config is supported by pipeline + @Override + boolean isGraphicsConfigSupported(GraphicsConfigTemplate3D gct, + GraphicsConfiguration gc) { + return true; + } + + // Methods to get actual capabilities from Canvas3D + @Override + boolean hasDoubleBuffer(Canvas3D cv) { + return true; + } + + @Override + boolean hasStereo(Canvas3D cv) { + return false; + } + + @Override + int getStencilSize(Canvas3D cv) { + return 0; + } + + @Override + boolean hasSceneAntialiasingMultisample(Canvas3D cv) { + return false; + } + + @Override + boolean hasSceneAntialiasingAccum(Canvas3D cv) { + return false; + } + + @Override + int getScreen(GraphicsDevice graphicsDevice) { + return 0; + } + + + // --------------------------------------------------------------------- + + // + // DrawingSurfaceObject methods + // + + // Method to construct a new DrawingSurfaceObject + @Override + DrawingSurfaceObject createDrawingSurfaceObject(Canvas3D cv) { + return new NoopDrawingSurfaceObject(cv); + } + + // Method to free the drawing surface object + @Override + void freeDrawingSurface(Canvas3D cv, DrawingSurfaceObject drawingSurfaceObject) { + // This method is a no-op + } + + // Method to free the native drawing surface object + @Override + void freeDrawingSurfaceNative(Object o) { + // This method is a no-op + } + + /** + * Dummy context for noop pipeline + */ + static class NoopContext implements Context { + } + +} diff --git a/src/javax/media/j3d/NotificationThread.java b/src/javax/media/j3d/NotificationThread.java new file mode 100644 index 0000000..c76c26d --- /dev/null +++ b/src/javax/media/j3d/NotificationThread.java @@ -0,0 +1,139 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +/** + * The NotificationThread class is used for asynchronous error notification, + * such as notifying ShaderError listeners. + */ +class NotificationThread 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 volatile boolean running = true; + private boolean waiting = false; + private boolean ready = false; + + private ArrayList notificationQueue = new ArrayList(); + + /** + * Creates a new instance of NotificationThread + */ + NotificationThread(ThreadGroup t) { + // Only one notification thread for the entire system + super(t, "J3D-NotificationThread"); + } + + /** + * Adds a notification message to the queue + */ + synchronized void addNotification(J3dNotification n) { + notificationQueue.add(n); + runMonitor(NOTIFY); + } + + /** + * Gets the list of queued notification messages + */ + private synchronized J3dNotification[] getNotifications() { + J3dNotification[] n = new J3dNotification[notificationQueue.size()]; + n = notificationQueue.toArray(n); + notificationQueue.clear(); + return n; + } + + /** + * Processes all pending notification messages + */ + private void processNotifications() { + J3dNotification[] notifications = getNotifications(); + + for (int i = 0; i < notifications.length; i++) { + J3dNotification n = notifications[i]; + switch (n.type) { + case J3dNotification.SHADER_ERROR: + n.universe.notifyShaderErrorListeners((ShaderError)n.args[0]); + break; + case J3dNotification.RENDERING_ERROR: + VirtualUniverse.notifyRenderingErrorListeners((RenderingError)n.args[0]); + break; + default: + System.err.println("J3dNotification.processNotifications: unrecognized type = " + n.type); + } + } + } + + // Called from MasterControlThread + void finish() { + runMonitor(STOP); + } + + @Override + public void run() { + while (running) { + runMonitor(WAIT); + + processNotifications(); + } +// System.err.println("Notification thread finished"); + } + + + private synchronized void runMonitor(int action) { + switch (action) { + case WAIT: + while (running && !ready) { + waiting = true; + try { + wait(); + } catch (InterruptedException e) { + } + waiting = false; + } + ready = false; + break; + case NOTIFY: + ready = true; + if (waiting) { + notify(); + } + break; + case STOP: + running = false; + notify(); + break; + default: + // Should never get here... + assert(false); + } + } + +} diff --git a/src/javax/media/j3d/ObjectUpdate.java b/src/javax/media/j3d/ObjectUpdate.java new file mode 100644 index 0000000..8a4e096 --- /dev/null +++ b/src/javax/media/j3d/ObjectUpdate.java @@ -0,0 +1,40 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/OrderedBin.java b/src/javax/media/j3d/OrderedBin.java new file mode 100644 index 0000000..545a56d --- /dev/null +++ b/src/javax/media/j3d/OrderedBin.java @@ -0,0 +1,123 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = setOCForCI.get(i).intValue(); + OrderedCollection oc = 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 = setOCForOI.get(i).intValue(); + OrderedCollection oc = 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/javax/media/j3d/OrderedChildInfo.java b/src/javax/media/j3d/OrderedChildInfo.java new file mode 100644 index 0000000..54f1f39 --- /dev/null +++ b/src/javax/media/j3d/OrderedChildInfo.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/OrderedCollection.java b/src/javax/media/j3d/OrderedCollection.java new file mode 100644 index 0000000..0b28118 --- /dev/null +++ b/src/javax/media/j3d/OrderedCollection.java @@ -0,0 +1,73 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + @Override + 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/javax/media/j3d/OrderedGroup.java b/src/javax/media/j3d/OrderedGroup.java new file mode 100644 index 0000000..4b50077 --- /dev/null +++ b/src/javax/media/j3d/OrderedGroup.java @@ -0,0 +1,486 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * 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 numChildren-1 (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 childIndexOrder[0] is rendered first, followed by + * childIndexOrder[1], and so on, with the child at + * childIndexOrder[numChildren-1] rendered + * last. + * + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_CHILD_INDEX_ORDER_READ + }; + + /** + * 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() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + + /** + * 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 numChildren-1 (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: + *

    + *
  • childIndexOrder.length != numChildren;
  • + *
  • childIndexOrder[i] < 0, + * for i in [0, numChildren-1];
  • + *
  • childIndexOrder[i] >= numChildren, + * for i in [0, numChildren-1];
  • + *
  • childIndexOrder[i] == + * childIndexOrder[j], + * for i,j in [0, numChildren-1], + * i != j;
  • + *
+ * + * @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 numChildren-1 + * (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 child 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: + *
    + *
  • childIndexOrder.length != numChildren;
  • + *
  • childIndexOrder[i] < 0, + * for i in [0, numChildren-1];
  • + *
  • childIndexOrder[i] >= numChildren, + * for i in [0, numChildren-1];
  • + *
  • childIndexOrder[i] == + * childIndexOrder[j], + * for i,j in [0, numChildren-1], + * i != j;
  • + *
+ * + * @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. + * + *

+ * 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 child has already + * been added as a child of another group node. + * + * @since Java 3D 1.3 + */ + @Override + 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 index + * must be a value + * greater than or equal to 0 and less than or equal to + * numChildren(). + * @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 child has already + * been added as a child of another group node. + * @exception IndexOutOfBoundsException if index is invalid. + * @exception IllegalStateException if the childIndexOrder array is + * not null. + * + * @since Java 3D 1.3 + */ + @Override + 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. + * + *

+ * 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 index + * must be a value + * greater than or equal to 0 and less than numChildren(). + * @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 index is invalid. + * + * @since Java 3D 1.3 + */ + @Override + 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. + * + *

+ * 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 + */ + @Override + 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. + * + *

+ * 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 + */ + @Override + 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. + * + *

+ * 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 + */ + @Override + 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. + */ + @Override + 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) { + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + OrderedGroup og = new OrderedGroup(); + og.duplicateNode(this, forceDuplicate); + return og; + } +} diff --git a/src/javax/media/j3d/OrderedGroupRetained.java b/src/javax/media/j3d/OrderedGroupRetained.java new file mode 100644 index 0000000..bcf4e36 --- /dev/null +++ b/src/javax/media/j3d/OrderedGroupRetained.java @@ -0,0 +1,527 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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(); + +// used to lock the orderedBin array +private final Object lockObj = new Object(); +// One OrderedBin per view +private OrderedBin[] orderedBin = new OrderedBin[0]; + + // 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 = new J3dMessage(); + 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; + } + + @Override + 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); + + } + + @Override + 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 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 + */ + @Override + 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 (lockObj) { + if (index < orderedBin.length) { + orderedBin[index] = ob; + return; + } + + // If we're clearing the entry to null, just return, don't bother + // expanding the array + if (ob == null) + return; + + OrderedBin[] newList = new OrderedBin[index + 1]; + System.arraycopy(orderedBin, 0, newList, 0, orderedBin.length); + orderedBin = newList; + orderedBin[index] = ob; + } +} + +// Get the orderedBin for this view index +OrderedBin getOrderedBin(int index) { + synchronized (lockObj) { + if (index >= orderedBin.length) + return null; + else + return orderedBin[index]; + } +} + + void updateChildIdTableInserted(int childId, int orderedId) { + int size = 0; + int i; + + //System.err.println("updateChildIdTableInserted childId " + childId + " orderedId " + orderedId + " " + this); + if (orderedChildIdTable != null) { + size = orderedChildIdTable.length; + for (i=0; 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 childId) { + orderedChildIdTable[i]--; // shift downward + } + else if (orderedChildIdTable[i] == childId) { + orderedChildIdTable[i] = -1; + //System.err.println("og.updateChildIdTableRemoved freeId " + i); + freeOrderedChildId(i); + } + } + } + + } + + @Override + void setAuxData(SetLiveState s, int index, int hkIndex) { + OrderedPath setLiveStateOrderedPath = s.orderedPaths.get(hkIndex); + for (int i=0; i= 0) { + setAuxData(s, j, hkIndex); + + } else { + MasterControl.getCoreLogger().severe("Can't Find matching hashKey in setNodeData."); + } + } + } + // Note s.orderedPaths is to be updated in GroupRetained.setLive + // for each of its children + } + + @Override + 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(1)); + } + + @Override + void appendChildrenData() { + childrenOrderedPaths.add(new ArrayList(1)); + } + + @Override + void doRemoveChild(int index, J3dMessage messages[], int messageIndex) { + + if(userChildIndexOrder != null) { + doRemoveChildIndexEntry(index); + } + + super.doRemoveChild(index, messages, messageIndex); + + } + + @Override + void removeChildrenData(int index) { + childrenOrderedPaths.remove(index); + } + + @Override + 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 = childrenOrderedPaths.get(childIndex); + if(child!=null) + child.setLive(s); + } + + @Override + void childCheckSetLive(NodeRetained child, int childIndex, + SetLiveState s, NodeRetained linkNode) { + ArrayList childOrderedPaths; + + if (linkNode != null) { + int ci = children.indexOf(linkNode); + childOrderedPaths = 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 = childrenOrderedPaths.get(childIndex); + + for(int i=0; i< orderedPaths.size();i++){ + OrderedPath childOrderedPath = orderedPaths.get(i).clonePath(); + childOrderedPath.addElementToPath(this, child.orderedId); + childOrderedPaths.add(childOrderedPath); + } + } + s.orderedPaths = childOrderedPaths; + child.setLive(s); + } +} diff --git a/src/javax/media/j3d/OrderedPath.java b/src/javax/media/j3d/OrderedPath.java new file mode 100644 index 0000000..0b34d5f --- /dev/null +++ b/src/javax/media/j3d/OrderedPath.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +class OrderedPath { +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 = new ArrayList(pathElements); + return path; +} + +void printPath() { + System.err.println("orderedPath: ["); + for (int i = 0; i < pathElements.size(); i++) { + OrderedPathElement ope = pathElements.get(i); + System.err.println("(" + ope.orderedGroup + "," + ope.childId); + } + System.err.println("]"); +} +} diff --git a/src/javax/media/j3d/OrderedPathElement.java b/src/javax/media/j3d/OrderedPathElement.java new file mode 100644 index 0000000..b4df84b --- /dev/null +++ b/src/javax/media/j3d/OrderedPathElement.java @@ -0,0 +1,38 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +class OrderedPathElement extends Object { + OrderedGroupRetained orderedGroup; + Integer childId; + + OrderedPathElement(OrderedGroupRetained og, Integer orderedId) { + orderedGroup = og; + childId = orderedId; + } +} diff --git a/src/javax/media/j3d/OrientedShape3D.java b/src/javax/media/j3d/OrientedShape3D.java new file mode 100644 index 0000000..0dd1418 --- /dev/null +++ b/src/javax/media/j3d/OrientedShape3D.java @@ -0,0 +1,692 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +/** + * 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 +z 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. + * + *

+ * 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. + * + *

+ * 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 +y axis of the + * TransformGroup with the +y axis in the View. + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * Note that in a multiple View system, picking and interestion test + * is done with the primary View only. + * + * @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; + + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_MODE_READ, + ALLOW_AXIS_READ, + ALLOW_POINT_READ, + ALLOW_SCALE_READ + }; + + /** + * Constructs an OrientedShape3D node with default parameters. + * The default values are as follows: + *

    + * alignment mode : ROTATE_ABOUT_AXIS
    + * alignment axis : Y-axis (0,1,0)
    + * rotation point : (0,0,1)
    + * constant scale enable : false
    + * scale : 1.0
    + *
+ */ + public OrientedShape3D() { + super(); + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + + /** + * Constructs an OrientedShape3D node with the specified geometry + * component, appearance component, mode, and axis. + * The specified axis must not be parallel to the Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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 Z + * axis--(0,0,z) for any value of z. It is not + * possible for the +Z 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,z). + * + * @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 cloneTree to duplicate the current node. + * cloneNode should be overridden by any user subclassed + * objects. All subclasses must have their cloneNode + * method consist of the following lines: + *

+     *     public Node cloneNode(boolean forceDuplicate) {
+     *         UserSubClass usc = new UserSubClass();
+     *         usc.duplicateNode(this, forceDuplicate);
+     *         return usc;
+     *     }
+     * 
+ * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + OrientedShape3D s = new OrientedShape3D(); + s.duplicateNode(this, forceDuplicate); + return s; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * Shape3D + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + + + /** + * Copies all Shape3D information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/OrientedShape3DRenderMethod.java b/src/javax/media/j3d/OrientedShape3DRenderMethod.java new file mode 100644 index 0000000..afa7ad8 --- /dev/null +++ b/src/javax/media/j3d/OrientedShape3DRenderMethod.java @@ -0,0 +1,115 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 { + + @Override + public boolean render(RenderMolecule rm, Canvas3D cv, + 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(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, + cv.screen.screen, + rm.textureBin.attributeBin.ignoreVertexColors); + 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(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, + cv.screen.screen, + rm.textureBin.attributeBin. + ignoreVertexColors); + isVisible = true; + } + } + else { + if (!VirtualUniverse.mc.viewFrustumCulling || + ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(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, + cv.screen.screen, + rm.textureBin.attributeBin. + ignoreVertexColors); + isVisible = true; + } + else { + cv.raIsVisible = false; + } + cv.ra = ra.renderAtom; + + } + ra = ra.next; + + } + return isVisible; + } +} diff --git a/src/javax/media/j3d/OrientedShape3DRetained.java b/src/javax/media/j3d/OrientedShape3DRetained.java new file mode 100644 index 0000000..4fe4026 --- /dev/null +++ b/src/javax/media/j3d/OrientedShape3DRetained.java @@ -0,0 +1,610 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector3f; +import javax.vecmath.Vector4d; + +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; + + private final Object transformLock = new Object(); + private 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: + *

    + * alignment mode : ROTATE_ABOUT_AXIS
    + * alignment axis : Y-axis (0,1,0)
    + * rotation point : (0,0,1)
    + *
+ */ + 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 = new J3dMessage(); + 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); + } + + @Override + 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 (transformLock) { + // check if the transforms list needs to be expanded + if (viewIndex >= orientedTransforms.length) { + Transform3D[] newList = new Transform3D[viewIndex + 1]; + System.arraycopy(orientedTransforms, 0, newList, 0, orientedTransforms.length); + orientedTransforms = newList; + } + + 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 if(mode == OrientedShape3D.ROTATE_ABOUT_POINT ){ // 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; + } + + @Override + 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; + } + + @Override + void searchGeometryAtoms(UnorderList list) { + list.add(getGeomAtom(getMirrorShape(key))); + } +} diff --git a/src/javax/media/j3d/PathInterpolator.java b/src/javax/media/j3d/PathInterpolator.java new file mode 100644 index 0000000..0d67bbe --- /dev/null +++ b/src/javax/media/j3d/PathInterpolator.java @@ -0,0 +1,292 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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 computePathInterpolation(alphaValue) 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 computePathInterpolation(alphaValue) 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 + * PathInterpolator(Alpha, TransformGroup, float[]) + */ + 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 + * computePathInterpolation(float) + */ + protected void computePathInterpolation() { + float value = this.alpha.value(); + computePathInterpolation(value); + } + + + /** + * Copies all PathInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/PhysicalBody.java b/src/javax/media/j3d/PhysicalBody.java new file mode 100644 index 0000000..7b1153c --- /dev/null +++ b/src/javax/media/j3d/PhysicalBody.java @@ -0,0 +1,368 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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: + *

    + * left eye position : (-0.033, 0.0, 0.0)
    + * right eye position : (0.033, 0.0, 0.0)
    + * left ear position : (-0.080, -0.030, 0.095)
    + * right ear position : (0.080, -0.030, 0.095)
    + * nominal eye height from ground : 1.68
    + * nominal eye offset from nominal screen : 0.4572
    + * head to head tracker transform : identity
    + *
+ */ + public PhysicalBody() { + // Just use the defaults + initHeadToHeadTracker(); + } + +// Add a user to the list of users +synchronized void removeUser(View view) { + users.remove(view); +} + +// Add a user to the list of users +synchronized void addUser(View view) { + if (!users.contains(view)) + 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 = users.get(i); + // XXXX: 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. + */ + + @Override + 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/javax/media/j3d/PhysicalEnvironment.java b/src/javax/media/j3d/PhysicalEnvironment.java new file mode 100644 index 0000000..e3d6b90 --- /dev/null +++ b/src/javax/media/j3d/PhysicalEnvironment.java @@ -0,0 +1,525 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +/** + * 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 final Hashtable physicalEnvMap; + +static { + physicalEnvMap = new Hashtable(); +} + /** + * Constructs a PhysicalEnvironment object with default parameters. + * The default values are as follows: + *
    + * sensor count : 3
    + * sensors : null (for all array elements)
    + * head index : 0
    + * right hand index : 1
    + * left hand index : 2
    + * dominant hand index : 1
    + * nondominant hand index : 2
    + * tracking available : false
    + * audio device : null
    + * input device list : empty
    + * coexistence to tracker base transform : identity
    + * coexistence center in pworld policy : View.NOMINAL_SCREEN
    + *
+ */ + public PhysicalEnvironment() { + this(3); + } + +// Add a user to the list of users +synchronized void removeUser(View view) { + users.remove(view); +} + +// Add a user to the list of users +synchronized void addUser(View view) { + if (!users.contains(view)) + users.add(view); +} + +// Add a user to the list of users +synchronized void notifyUsers() { + for (int i = users.size() - 1; i >= 0; i--) { + users.get(i).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 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) { + synchronized (devices) { + devices.remove(device); + 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/javax/media/j3d/PickBounds.java b/src/javax/media/j3d/PickBounds.java new file mode 100644 index 0000000..60d1ec1 --- /dev/null +++ b/src/javax/media/j3d/PickBounds.java @@ -0,0 +1,108 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; + +/** + * 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. + */ + @Override + 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. + @Override + 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; + } + + @Override + Point3d getStartPoint() { + return bounds.getCenter(); + } + + @Override + int getPickType() { + return (bounds != null ? bounds.getPickType() : + PickShape.PICKUNKNOWN); + } +} diff --git a/src/javax/media/j3d/PickCone.java b/src/javax/media/j3d/PickCone.java new file mode 100644 index 0000000..6dd3893 --- /dev/null +++ b/src/javax/media/j3d/PickCone.java @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.Vector3d; + +/** + * 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 PI/64. + */ + 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. + */ + @Override + abstract boolean intersect(Bounds bounds, Point4d pickPos); + + @Override + Point3d getStartPoint() { + return origin; + } + + @Override + int getPickType() { + return PICKCONE; + } +} diff --git a/src/javax/media/j3d/PickConeRay.java b/src/javax/media/j3d/PickConeRay.java new file mode 100644 index 0000000..d853508 --- /dev/null +++ b/src/javax/media/j3d/PickConeRay.java @@ -0,0 +1,280 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.Vector3d; + +/** + * 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 PI/64 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). + */ + @Override + 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 = Utils.ptToRaySquare(sphCenter, origin, direction, rayPt); + 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 = Utils.ptToRaySquare(center, origin, direction, rayPt); + + 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 (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;iPI/64 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). + */ + @Override + 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 = Utils.ptToSegSquare(sphCenter, origin, end, rayPt); + + 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 = Utils.ptToSegSquare(center, origin, end, rayPt); + + 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 (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= 0) { + p = ptope.planes[i--]; + if (( x*p.x + y*p.y + z*p.z + p.w ) > Bounds.EPSILON) { + return false; + } + } + return true; + } + + @Override + Point3d getStartPoint() { + return origin; + } + + @Override + int getPickType() { + return PICKCYLINDER; + } +} diff --git a/src/javax/media/j3d/PickCylinderRay.java b/src/javax/media/j3d/PickCylinderRay.java new file mode 100644 index 0000000..62195dd --- /dev/null +++ b/src/javax/media/j3d/PickCylinderRay.java @@ -0,0 +1,262 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Point4d; +import javax.vecmath.Vector3d; + +/** + * 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). + */ + @Override + 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 = Utils.ptToRaySquare(sphCenter, origin, direction, null); + 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 = Utils.ptToRaySquare(center, origin, direction, null); + + 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 (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 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 (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 + * + * @see Locale + * @see BranchGroup + * + * @since Java 3D 1.4 + */ + + +public class PickInfo extends Object { + + static final int PICK_ALL = 1; + + static final int PICK_ANY = 2; + + /* The SceneGraphPath of the intersected pickable item */ + private SceneGraphPath sgp; + + /* The intersected pickable node object */ + private Node node; + + /* A copy of LocalToVworld transform of the pickable node */ + private Transform3D l2vw; + + /* The closest intersection point */ + private Point3d closestIntersectionPoint; + + /* Distance between start point of pickShape and closest intersection point */ + private double closestDistance; + + /* An array to store intersection results */ + private IntersectionInfo[] intersectionInfoArr; + + /* The following references are for internal geometry computation use only */ + private ArrayList intersectionInfoList = new ArrayList(); + private boolean intersectionInfoListSorted = false; + private Transform3D l2vwRef; + private Node nodeRef; + + /** + * Specifies a Pick using the bounds of the pickable nodes. + */ + public static final int PICK_BOUNDS = 1; + + /** + * Specifies a Pick using the geometry of the pickable nodes. + */ + public static final int PICK_GEOMETRY = 2; + + /** + * Specifies that this PickInfo returns the computed SceneGraphPath object. + */ + public static final int SCENEGRAPHPATH = 0x01; + + /** + * Specifies that this PickInfo returns the computed intersected Node object. + */ + public static final int NODE = 0x02; + + /** + * Specifies that this PickInfo returns the computed local to vworld transform. + */ + public static final int LOCAL_TO_VWORLD = 0x04; + + /** + * Specifies that this PickInfo returns the closest intersection point. + */ + public static final int CLOSEST_INTERSECTION_POINT = 0x08; + + /** + * Specifies that this PickInfo returns the closest intersection distance. + */ + public static final int CLOSEST_DISTANCE = 0x10; + + /** + * Specifies that this PickInfo returns only the closest intersection + * geometry information. + */ + public static final int CLOSEST_GEOM_INFO = 0x20; + + /** + * Specifies that this PickInfo returns all the closest intersection + * geometry informations. + */ + public static final int ALL_GEOM_INFO = 0x40; + + + /** PickInfo Constructor */ + PickInfo() { + + } + + void setSceneGraphPath(SceneGraphPath sgp) { + this.sgp = sgp; + } + + void setNode(Node node) { + this.node = node; + } + + void setLocalToVWorld(Transform3D l2vw) { + this.l2vw = l2vw; + } + + void setClosestIntersectionPoint(Point3d cIPt) { + this.closestIntersectionPoint = cIPt; + } + + void setClosestDistance(double cDist) { + this.closestDistance = cDist; + } + + void setLocalToVWorldRef(Transform3D l2vwRef) { + this.l2vwRef = l2vwRef; + } + + void setNodeRef(Node nodeRef) { + this.nodeRef = nodeRef; + } + + IntersectionInfo createIntersectionInfo() { + return new IntersectionInfo(); + } + + void insertIntersectionInfo(IntersectionInfo iInfo) { + intersectionInfoList.add(iInfo); + intersectionInfoListSorted = false; + } + + void sortIntersectionInfoArray(IntersectionInfo[] iInfoArr) { + + class Sort { + + IntersectionInfo iInfoArr[]; + + Sort(IntersectionInfo[] iInfoArr) { + // System.err.println("Sort IntersectionInfo ..."); + this.iInfoArr = iInfoArr; + } + + void sorting() { + if (iInfoArr.length < 7) { + // System.err.println(" -- insertSort."); + insertSort(); + } else { + // System.err.println(" -- quicksort."); + quicksort(0, iInfoArr.length-1); + } + } + + // Insertion sort on smallest arrays + final void insertSort() { + for (int i=0; i0 && + (iInfoArr[j-1].distance > iInfoArr[j].distance); j--) { + IntersectionInfo iInfo = iInfoArr[j]; + iInfoArr[j] = iInfoArr[j-1]; + iInfoArr[j-1] = iInfo; + } + } + } + + final void quicksort( int l, int r ) { + int i = l; + int j = r; + double k = iInfoArr[(l+r) / 2].distance; + + do { + while (iInfoArr[i].distance0 && + (pIArr[j-1].closestDistance > pIArr[j].closestDistance); j--) { + PickInfo pI = pIArr[j]; + pIArr[j] = pIArr[j-1]; + pIArr[j-1] = pI; + } + } + } + + final void quicksort( int l, int r ) { + int i = l; + int j = r; + double k = pIArr[(l+r) / 2].closestDistance; + + do { + while (pIArr[i].closestDistanceN
IntersectionInfo objects containing all intersections of + * the picked node in sorted order if flag is to set ALL_GEOM_INFO, or null if neither + * bit is set. + * @see Locale + * @see BranchGroup + */ + public IntersectionInfo[] getIntersectionInfos() { + if (intersectionInfoListSorted == false) { + intersectionInfoArr = new IntersectionInfo[intersectionInfoList.size()]; + intersectionInfoArr = intersectionInfoList.toArray(intersectionInfoArr); + + sortIntersectionInfoArray(intersectionInfoArr); + } + + return intersectionInfoArr; + } + +/** + * 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 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; +} + + static private Node[] createPath(NodeRetained srcNode, + BranchGroupRetained bgRetained, + GeometryAtom geomAtom, + ArrayList initpath) { + + ArrayList path = retrievePath(srcNode, bgRetained, + geomAtom.source.key); + assert(path != null); + + return mergePath(path, initpath); + + } + + + /** + * 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; + } + + /** + * search the full path from the bottom 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 = parents.get(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)p1.get(i).source; + } + for (int j=0; i< len; i++, j++) { + nodes[l-i] = (Node)p2.get(j).source; + } + return nodes; + } + + /** + * Sort the GeometryAtoms distance from shape in ascending order + * geomAtoms.length must be >= 1 + */ + static 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; i0 && + (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] getPickInfos(ArrayList initpath, + BranchGroupRetained bgRetained, + GeometryAtom geomAtoms[], + Locale locale, int flags, int pickType) { + + ArrayList pickInfoList = new ArrayList(5); + NodeRetained srcNode; + ArrayList text3dList = null; + + if ((geomAtoms == null) || (geomAtoms.length == 0)) { + return null; + } + + for (int i=0; i < geomAtoms.length; i++) { + assert((geomAtoms[i] != null) && + (geomAtoms[i].source != null)); + + PickInfo pickInfo = null; + Shape3DRetained shape = geomAtoms[i].source; + srcNode = shape.sourceNode; + + // Fix to Issue 274 : NPE With Simultaneous View and Content Side PickingBehaviors + // This node isn't under the selected BG for pick operation. + if (!inside(shape.branchGroupPath,bgRetained)) { + continue; + } + + if (srcNode == 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 (srcNode instanceof Shape3DRetained) { + Shape3DRetained s3dR = (Shape3DRetained) srcNode; + GeometryRetained geomR = null; + for(int cnt=0; cnt pickInfoList = null; + + if (node instanceof Locale) { + locale = (Locale) node; + } + else if ( node instanceof BranchGroupRetained) { + bgRetained = (BranchGroupRetained) node; + locale = bgRetained.locale; + } + synchronized (locale.universe.sceneGraphLock) { + ArrayList initPath = null; + if ( bgRetained != null) { + initPath = initSceneGraphPath(bgRetained); + } + pickInfoList = getPickInfos(initPath, bgRetained, geomAtoms, + locale, flags, pickType); + } + + // We're done with PICK_BOUNDS case, but there is still more work for PICK_GEOMETRY case. + if((mode == PICK_GEOMETRY) && (pickInfoList != null) && + ((pickInfoListSize = pickInfoList.size()) > 0)) { + + //System.err.println("PickInfo.pick() - In geometry case : pickInfoList.size() is " + pickInfoListSize); + Node pickNode = null; + + // Order is impt. Need to do in reverse order. + for(int i = pickInfoListSize - 1; i >= 0; i--) { + PickInfo pickInfo = pickInfoList.get(i); + + pickNode = pickInfo.getNode(); + if( pickNode == null) { + // Use the piggy reference from getPickInfos() + pickNode = pickInfo.getNodeRef(); + } + + if (pickNode instanceof Shape3D) { + + /* + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are + * as follows : + * + * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ + * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ + * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above) + * + */ + + if (!pickNode.getCapability(Shape3D.ALLOW_GEOMETRY_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo0")); + } + + for (int j = 0; j < ((Shape3D)pickNode).numGeometries(); j++) { + Geometry geo = ((Shape3D)pickNode).getGeometry(j); + + if(geo == null) { + continue; + } + + if(!geo.getCapability(Geometry.ALLOW_INTERSECT)) { + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo1")); + } + + if (geo instanceof GeometryArray) { + if(!geo.getCapability(GeometryArray.ALLOW_COORDINATE_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo2")); + if(!geo.getCapability(GeometryArray.ALLOW_COUNT_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo3")); + if(!geo.getCapability(GeometryArray.ALLOW_FORMAT_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo4")); + if (geo instanceof IndexedGeometryArray) { + if(!geo.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo5")); + } + } else if (geo instanceof CompressedGeometry) { + if(!geo.getCapability(CompressedGeometry.ALLOW_GEOMETRY_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo0")); + } + } + + if (((Shape3DRetained)(pickNode.retained)).intersect(pickInfo, pickShape, flags) == false) { + // System.err.println(" ---- geom " + i + " not intersected"); + + pickInfoList.remove(i); + + } + else if(pickType == PICK_ANY) { + pickInfoArr = new PickInfo[1]; + pickInfoArr[0] = pickInfo; + return pickInfoArr; + } + } else if (pickNode instanceof Morph) { + + /* + * @exception CapabilityNotSetException if the mode is + * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit + * is not set in any Geometry objects referred to by any shape + * node whose bounds intersects the PickShape. + * + * @exception CapabilityNotSetException if flags contains any of + * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO + * or ALL_GEOM_INFO, and the capability bits that control reading of + * coordinate data are not set in any GeometryArray object referred + * to by any shape node that intersects the PickShape. + * The capability bits that must be set to avoid this exception are + * as follows : + * + * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ + * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ + * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ + * (in addition to one of the above) + * + */ + + if (!pickNode.getCapability(Morph.ALLOW_GEOMETRY_ARRAY_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo6")); + } + + int numGeo = ((MorphRetained)(pickNode.retained)).getNumGeometryArrays(); + for (int j = 0; j < numGeo; j++) { + GeometryArray geo = ((Morph)pickNode).getGeometryArray(j); + + if(geo == null) { + continue; + } + + if(!geo.getCapability(Geometry.ALLOW_INTERSECT)) { + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo1")); + } + + if(!geo.getCapability(GeometryArray.ALLOW_COORDINATE_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo2")); + if(!geo.getCapability(GeometryArray.ALLOW_COUNT_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo3")); + if(!geo.getCapability(GeometryArray.ALLOW_FORMAT_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo4")); + + if (geo instanceof IndexedGeometryArray) { + if(!geo.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("PickInfo5")); + } + } + + if (((MorphRetained)(pickNode.retained)).intersect(pickInfo, pickShape, flags) == false) { + pickInfoList.remove(i); + } + else if(pickType == PICK_ANY) { + pickInfoArr = new PickInfo[1]; + pickInfoArr[0] = pickInfo; + return pickInfoArr; + } + } + } + } + + // System.err.println("PickInfo : pickInfoList " + pickInfoList); + + if ((pickInfoList != null) && (pickInfoList.size() > 0)) { + // System.err.println(" --- : pickInfoList.size() " + pickInfoList.size()); + // System.err.println(" --- : pickInfoList's sgp " + + // ((PickInfo)(pickInfoList.get(0))).getSceneGraphPath()); + pickInfoArr = new PickInfo[pickInfoList.size()]; + return pickInfoList.toArray(pickInfoArr); + } + + return null; + + } + + /** + * The IntersectionInfo object holds extra information about an intersection + * of a PickShape with a Node as part of a PickInfo. Information such as + * the intersected geometry, the intersected point, and the vertex indices + * can be inquired. + * The local coordinates, normal, color and texture coordiantes of at the + * intersection can be computed, if they are present and readable, using the + * interpolation weights and vertex indices. + *

+ * If the Shape3D being picked has multiple geometry arrays, the possible arrays + * of IntersectionInfo are stored in the PickInfo and referred to by a geometry + * index. If the picked geometry is of type, Text3D or CompressGeometry, + * getVertexIndices is invalid. If the picked Node is an Morph + * object, the geometry used in pick computation is alway at index 0. + *

+ * + * @since Java 3D 1.4 + */ + + public class IntersectionInfo extends Object { + + /* The index to the intersected geometry in the pickable node */ + private int geomIndex; + + /* The reference to the intersected geometry in the pickable object */ + private Geometry geom; + + /* The intersection point */ + private Point3d intersectionPoint; + + /* Distance between start point of pickShape and intersection point */ + private double distance; + + /* The vertex indices of the intersected primitive in the geometry */ + private int[] vertexIndices; + + /* The interpolation weights for each of the verticies of the primitive */ + // private float[] weights; Not supported. Should be done in util. package + + /** IntersectionInfo Constructor */ + IntersectionInfo() { + + } + + void setGeometryIndex(int geomIndex) { + this.geomIndex = geomIndex; + } + + void setGeometry(Geometry geom) { + this.geom = geom; + } + + void setIntersectionPoint(Point3d intersectionPoint) { + assert(intersectionPoint != null); + this.intersectionPoint = new Point3d(intersectionPoint); + } + + void setDistance(double distance) { + this.distance = distance; + } + + void setVertexIndices(int[] vertexIndices) { + assert(vertexIndices != null); + this.vertexIndices = new int[vertexIndices.length]; + for(int i=0; i { +private final Type pipeType; + +PipelineCreator(Type type) { + pipeType = type; +} + +@Override +public Pipeline run() { + try { + switch (pipeType) { + case JOGL: + return (Pipeline)Class.forName("javax.media.j3d.JoglPipeline").newInstance(); + case NOOP: + return (Pipeline)Class.forName("javax.media.j3d.NoopPipeline").newInstance(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; +} +} + + /** + * Initialize the Pipeline. Called exactly once by + * MasterControl.loadLibraries() to create the singleton + * Pipeline object. + */ + static void createPipeline(Type pipelineType) { + pipeline = java.security.AccessController.doPrivileged(new PipelineCreator(pipelineType)); + pipeline.initialize(pipelineType); + } + + /** + * Returns the singleton Pipeline object. + */ + static Pipeline getPipeline() { + return pipeline; + } + + /** + * Initializes the pipeline object. Only called by initPipeline. + * Pipeline subclasses may override this, but must call + * super.initialize(pipelineType); + */ + void initialize(Type pipelineType) { + setPipelineType(pipelineType); + } + + /** + * Sets the pipeline type. Only called by initialize. + */ + private void setPipelineType(Type pipelineType) { + this.pipelineType = pipelineType; + } + + /** + * Returns the pipeline type + */ + Type getPipelineType() { + return pipelineType; + } + + /** + * Returns the pipeline name + */ + String getPipelineName() { + switch (pipelineType) { + case JOGL: + return "JOGL"; + case NOOP: + return "NOOP"; + default: + // Should not get here + throw new AssertionError("missing case statement"); + } + } + + /** + * Returns the renderer name + */ + String getRendererName() { + switch (pipelineType) { + case JOGL: + return "OpenGL"; + case NOOP: + return "None"; + default: + // Should not get here + throw new AssertionError("missing case statement"); + } + } + + // --------------------------------------------------------------------- + + // + // GeometryArrayRetained methods + // + + // used for GeometryArrays by Copy or interleaved + abstract void execute(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int startVIndex, int vcount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + int vertexAttrCount, int[] vertexAttrSizes, + float[] varray, float[] cdata, int cdirty); + + // used by GeometryArray by Reference with java arrays + abstract void executeVA(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + 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 vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndex, float[][] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int[] texIndex, int texstride, Object[] texCoords, + int cdirty); + + // used by GeometryArray by Reference with NIO buffer + abstract void executeVABuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int vcount, + int vformat, + int vdefined, + int coordIndex, + Buffer vcoords, + int colorIndex, + Buffer cdataBuffer, + float[] cfdata, byte[] cbdata, + int normalIndex, FloatBuffer ndata, + int vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndex, FloatBuffer[] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int[] texIndex, int texstride, Object[] texCoords, + int cdirty); + + // used by GeometryArray by Reference in interleaved format with NIO buffer + abstract void executeInterleavedBuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int startVIndex, int vcount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + FloatBuffer varray, float[] cdata, int cdirty); + + abstract void setVertexFormat(Context ctx, GeometryArrayRetained geo, + int vformat, boolean useAlpha, boolean ignoreVertexColors); + + // used for GeometryArrays + abstract void buildGA(Context 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, + int vertexAttrCount, int[] vertexAttrSizes, + double[] xform, double[] nxform, + float[] varray); + + // used to Build Dlist GeometryArray by Reference with java arrays + abstract void buildGAForByRef(Context 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 vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndex, float[][] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int[] texIndex, int texstride, Object[] texCoords, + double[] xform, double[] nxform); + + // used to Build Dlist GeometryArray by Reference with NIO buffer + // NOTE: NIO buffers are no longer supported in display lists. We + // have no plans to add this support. + /* + abstract void buildGAForBuffer(Context 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); + */ + + + // --------------------------------------------------------------------- + + // + // IndexedGeometryArrayRetained methods + // + + // by-copy or interleaved, by reference, Java arrays + abstract void executeIndexedGeometry(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + float[] varray, float[] cdata, + int cdirty, + int[] indexCoord); + + // interleaved, by reference, nio buffer + abstract void executeIndexedGeometryBuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean useAlpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int indexCount, + int vertexCount, int vformat, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetOffset, + int numActiveTexUnitState, + FloatBuffer varray, float[] cdata, + int cdirty, + int[] indexCoord); + + // non interleaved, by reference, Java arrays + abstract void executeIndexedGeometryVA(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vdefined, + float[] vfcoords, double[] vdcoords, + float[] cfdata, byte[] cbdata, + float[] ndata, + int vertexAttrCount, int[] vertexAttrSizes, + float[][] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int texstride, Object[] texCoords, + int cdirty, + int[] indexCoord); + + // non interleaved, by reference, nio buffer + abstract void executeIndexedGeometryVABuffer(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vdefined, + Buffer vcoords, + Buffer cdataBuffer, + float[] cfdata, byte[] cbdata, + FloatBuffer normal, + int vertexAttrCount, int[] vertexAttrSizes, + FloatBuffer[] vertexAttrData, + int texcoordmaplength, + int[] texcoordoffset, + int numActiveTexUnitState, + int texstride, Object[] texCoords, + int cdirty, + int[] indexCoord); + + // by-copy geometry + abstract void buildIndexedGeometry(Context ctx, + GeometryArrayRetained geo, int geo_type, + boolean isNonUniformScale, boolean updateAlpha, + float alpha, + boolean ignoreVertexColors, + int initialIndexIndex, + int validIndexCount, + int vertexCount, + int vformat, + int vertexAttrCount, int[] vertexAttrSizes, + int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, + int[] texCoordSetMapOffset, + double[] xform, double[] nxform, + float[] varray, int[] indexCoord); + + + // --------------------------------------------------------------------- + + // + // GraphicsContext3D methods + // + + // Native method for readRaster + abstract void readRaster(Context ctx, + int type, int xSrcOffset, int ySrcOffset, + int width, int height, int hCanvas, + int imageDataType, + int imageFormat, + Object imageBuffer, + int depthFormat, + Object depthBuffer); + + + // --------------------------------------------------------------------- + + // + // GLSLShaderProgramRetained methods + // + + // ShaderAttributeValue methods + + abstract ShaderError setGLSLUniform1i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int value); + + abstract ShaderError setGLSLUniform1f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float value); + + abstract ShaderError setGLSLUniform2i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value); + + abstract ShaderError setGLSLUniform2f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setGLSLUniform3i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value); + + abstract ShaderError setGLSLUniform3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setGLSLUniform4i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value); + + abstract ShaderError setGLSLUniform4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setGLSLUniformMatrix3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setGLSLUniformMatrix4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + // ShaderAttributeArray methods + + abstract ShaderError setGLSLUniform1iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setGLSLUniform1fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setGLSLUniform2iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setGLSLUniform2fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setGLSLUniform3iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setGLSLUniform3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setGLSLUniform4iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setGLSLUniform4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setGLSLUniformMatrix3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setGLSLUniformMatrix4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + // abstract interfaces for shader compilation, etc. + abstract ShaderError createGLSLShader(Context ctx, int shaderType, ShaderId[] shaderId); + abstract ShaderError destroyGLSLShader(Context ctx, ShaderId shaderId); + abstract ShaderError compileGLSLShader(Context ctx, ShaderId shaderId, String program); + + abstract ShaderError createGLSLShaderProgram(Context ctx, ShaderProgramId[] shaderProgramId); + abstract ShaderError destroyGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId); + abstract ShaderError linkGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId, + ShaderId[] shaderIds); + abstract ShaderError bindGLSLVertexAttrName(Context ctx, ShaderProgramId shaderProgramId, + String attrName, int attrIndex); + abstract void lookupGLSLShaderAttrNames(Context ctx, ShaderProgramId shaderProgramId, + int numAttrNames, String[] attrNames, ShaderAttrLoc[] locArr, + int[] typeArr, int[] sizeArr, boolean[] isArrayArr); + + abstract ShaderError useGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId); + + + // --------------------------------------------------------------------- + + // + // ColoringAttributesRetained methods + // + + abstract void updateColoringAttributes(Context ctx, + float dRed, float dGreen, float dBlue, + float red, float green, float blue, + float alpha, + boolean lEnable, + int shadeModel); + + + // --------------------------------------------------------------------- + + // + // DirectionalLightRetained methods + // + + abstract void updateDirectionalLight(Context ctx, + int lightSlot, float red, float green, + float blue, float x, float y, float z); + + + // --------------------------------------------------------------------- + + // + // PointLightRetained methods + // + + abstract void updatePointLight(Context ctx, + int lightSlot, float red, float green, + float blue, float ax, float ay, float az, + float px, float py, float pz); + + + // --------------------------------------------------------------------- + + // + // SpotLightRetained methods + // + + abstract void updateSpotLight(Context 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); + + + // --------------------------------------------------------------------- + + // + // ExponentialFogRetained methods + // + + abstract void updateExponentialFog(Context ctx, + float red, float green, float blue, + float density); + + + // --------------------------------------------------------------------- + + // + // LinearFogRetained methods + // + + abstract void updateLinearFog(Context ctx, + float red, float green, float blue, + double fdist, double bdist); + + + // --------------------------------------------------------------------- + + // + // LineAttributesRetained methods + // + + abstract void updateLineAttributes(Context ctx, + float lineWidth, int linePattern, + int linePatternMask, + int linePatternScaleFactor, + boolean lineAntialiasing); + + + // --------------------------------------------------------------------- + + // + // MaterialRetained methods + // + + abstract void updateMaterial(Context 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); + + + // --------------------------------------------------------------------- + + // + // ModelClipRetained methods + // + + abstract void updateModelClip(Context ctx, int planeNum, boolean enableFlag, + double A, double B, double C, double D); + + + // --------------------------------------------------------------------- + + // + // PointAttributesRetained methods + // + + abstract void updatePointAttributes(Context ctx, float pointSize, boolean pointAntialiasing); + + + // --------------------------------------------------------------------- + + // + // PolygonAttributesRetained methods + // + + abstract void updatePolygonAttributes(Context ctx, + int polygonMode, int cullFace, + boolean backFaceNormalFlip, + float polygonOffset, + float polygonOffsetFactor); + + + // --------------------------------------------------------------------- + + // + // RenderingAttributesRetained methods + // + + abstract void updateRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride, + boolean depthBufferEnable, + boolean depthBufferWriteEnable, + int depthTestFunction, + float alphaTestValue, int alphaTestFunction, + boolean ignoreVertexColors, + boolean rasterOpEnable, int rasterOp, + boolean userStencilAvailable, boolean stencilEnable, + int stencilFailOp, int stencilZFailOp, int stencilZPassOp, + int stencilFunction, int stencilReferenceValue, + int stencilCompareMask, int stencilWriteMask ); + + + // --------------------------------------------------------------------- + + // + // TexCoordGenerationRetained methods + // + + /** + * This method updates the native context: + * trans contains eyeTovworld transform in d3d + * trans contains vworldToEye transform in ogl + */ + abstract void updateTexCoordGeneration(Context 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); + + + // --------------------------------------------------------------------- + + // + // TransparencyAttributesRetained methods + // + + abstract void updateTransparencyAttributes(Context ctx, + float alpha, int geometryType, + int polygonMode, + boolean lineAA, boolean pointAA, + int transparencyMode, + int srcBlendFunction, + int dstBlendFunction); + + + // --------------------------------------------------------------------- + + // + // TextureAttributesRetained methods + // + + abstract void updateTextureAttributes(Context ctx, + double[] transform, boolean isIdentity, int textureMode, + int perspCorrectionMode, float red, + float green, float blue, float alpha, + int textureFormat); + + abstract void updateRegisterCombiners(Context 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); + + abstract void updateTextureColorTable(Context ctx, int numComponents, + int colorTableSize, + int[] colorTable); + + abstract void updateCombiner(Context ctx, + int combineRgbMode, int combineAlphaMode, + int[] combineRgbSrc, int[] combineAlphaSrc, + int[] combineRgbFcn, int[] combineAlphaFcn, + int combineRgbScale, int combineAlphaScale); + + + // --------------------------------------------------------------------- + + // + // TextureUnitStateRetained methods + // + + abstract void updateTextureUnitState(Context ctx, int unitIndex, boolean enableFlag); + + + // --------------------------------------------------------------------- + + // + // TextureRetained methods + // Texture2DRetained methods + // + + abstract void bindTexture2D(Context ctx, int objectId, boolean enable); + + abstract void updateTexture2DImage(Context ctx, + int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int imageDataType, Object data, boolean useAutoMipMap); + + abstract void updateTexture2DSubImage(Context ctx, + int level, int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object data, boolean useAutoMipMap); + + abstract void updateTexture2DLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod); + + abstract void updateTexture2DLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ); + + abstract void updateTexture2DBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha); + + abstract void updateTexture2DFilterModes(Context ctx, + int minFilter, int magFilter); + + abstract void updateTexture2DSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts); + + abstract void updateTexture2DFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts); + + abstract void updateTexture2DAnisotropicFilter(Context ctx, float degree); + + + // --------------------------------------------------------------------- + + // + // Texture3DRetained methods + // + + abstract void bindTexture3D(Context ctx, int objectId, boolean enable); + + abstract void updateTexture3DImage(Context ctx, + int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, int depth, + int boundaryWidth, + int imageDataType, Object imageData, boolean useAutoMipMap); + + abstract void updateTexture3DSubImage(Context ctx, + int level, + int xoffset, int yoffset, int zoffset, + int textureFormat, int imageFormat, + int imgXoffset, int imgYoffset, int imgZoffset, + int tilew, int tileh, + int width, int height, int depth, + int imageDataType, Object imageData, boolean useAutoMipMap); + + abstract void updateTexture3DLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod); + + abstract void updateTexture3DLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ); + + abstract void updateTexture3DBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + int boundaryModeR, float boundaryRed, + float boundaryGreen, float boundaryBlue, + float boundaryAlpha); + + abstract void updateTexture3DFilterModes(Context ctx, + int minFilter, int magFilter); + + abstract void updateTexture3DSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts); + + abstract void updateTexture3DFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts); + + abstract void updateTexture3DAnisotropicFilter(Context ctx, float degree); + + + // --------------------------------------------------------------------- + + // + // TextureCubeMapRetained methods + // + + abstract void bindTextureCubeMap(Context ctx, int objectId, boolean enable); + + abstract void updateTextureCubeMapImage(Context ctx, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int imageDataType, Object imageData, boolean useAutoMipMap); + + abstract void updateTextureCubeMapSubImage(Context ctx, + int face, int level, int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object imageData, boolean useAutoMipMap); + + abstract void updateTextureCubeMapLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod); + + abstract void updateTextureCubeMapLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ); + + abstract void updateTextureCubeMapBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha); + + abstract void updateTextureCubeMapFilterModes(Context ctx, + int minFilter, int magFilter); + + abstract void updateTextureCubeMapSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts); + + abstract void updateTextureCubeMapFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts); + + abstract void updateTextureCubeMapAnisotropicFilter(Context ctx, float degree); + + // --------------------------------------------------------------------- + + // + // MasterControl methods + // + + // Maximum lights supported by the native API + abstract int getMaximumLights(); + + + // --------------------------------------------------------------------- + + // + // Canvas3D methods - native wrappers + // + + // This is the native method for creating the underlying graphics context. + abstract Context createNewContext(Canvas3D cv, Drawable drawable, + Context shareCtx, boolean isSharedCtx, + boolean offScreen); + + abstract void createQueryContext(Canvas3D cv, Drawable drawable, + boolean offScreen, int width, int height); + + // This is the native for creating offscreen buffer + abstract Drawable createOffScreenBuffer(Canvas3D cv, Context ctx, int width, int height); + + abstract void destroyOffScreenBuffer(Canvas3D cv, Context ctx, Drawable drawable); + + // This is the native for reading the image from the offscreen buffer + abstract void readOffScreenBuffer(Canvas3D cv, Context ctx, int format, int type, Object data, int width, int height); + + // The native method for swapBuffers + abstract void swapBuffers(Canvas3D cv, Context ctx, Drawable drawable); + + // native method for setting Material when no material is present + abstract void updateMaterialColor(Context ctx, float r, float g, float b, float a); + + abstract void destroyContext(Drawable drawable, Context ctx); + + // This is the native method for doing accumulation. + abstract void accum(Context ctx, float value); + + // This is the native method for doing accumulation return. + abstract void accumReturn(Context ctx); + + // This is the native method for clearing the accumulation buffer. + abstract void clearAccum(Context ctx); + + // This is the native method for getting the number of lights the underlying + // native library can support. + abstract int getNumCtxLights(Context ctx); + + // Native method for decal 1st child setup + abstract boolean decal1stChildSetup(Context ctx); + + // Native method for decal nth child setup + abstract void decalNthChildSetup(Context ctx); + + // Native method for decal reset + abstract void decalReset(Context ctx, boolean depthBufferEnable); + + // Native method for decal reset + abstract void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable); + + // The following three methods are used in multi-pass case + + // native method for setting blend color + abstract void setBlendColor(Context ctx, float red, float green, + float blue, float alpha); + + // native method for setting blend func + abstract void setBlendFunc(Context ctx, int src, int dst); + + // native method for setting fog enable flag + abstract void setFogEnableFlag(Context ctx, boolean enableFlag); + + // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported + abstract void setFullSceneAntialiasing(Context ctx, boolean enable); + + // Native method to update separate specular color control + abstract void updateSeparateSpecularColorEnable(Context ctx, boolean control); + + // True under Solaris, + // False under windows when display mode <= 8 bit + abstract boolean validGraphicsMode(); + + // native method for setting light enables + abstract void setLightEnables(Context ctx, long enableMask, int maxLights); + + // native method for setting scene ambient + abstract void setSceneAmbient(Context ctx, float red, float green, float blue); + + // native method for disabling fog + abstract void disableFog(Context ctx); + + // native method for disabling modelClip + abstract void disableModelClip(Context ctx); + + // native method for setting default RenderingAttributes + abstract void resetRenderingAttributes(Context ctx, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride); + + // native method for setting default texture + abstract void resetTextureNative(Context ctx, int texUnitIndex); + + // native method for activating a particular texture unit + abstract void activeTextureUnit(Context ctx, int texUnitIndex); + + // native method for setting default TexCoordGeneration + abstract void resetTexCoordGeneration(Context ctx); + + // native method for setting default TextureAttributes + abstract void resetTextureAttributes(Context ctx); + + // native method for setting default PolygonAttributes + abstract void resetPolygonAttributes(Context ctx); + + // native method for setting default LineAttributes + abstract void resetLineAttributes(Context ctx); + + // native method for setting default PointAttributes + abstract void resetPointAttributes(Context ctx); + + // native method for setting default TransparencyAttributes + abstract void resetTransparency(Context ctx, int geometryType, + int polygonMode, boolean lineAA, + boolean pointAA); + + // native method for setting default ColoringAttributes + abstract void resetColoringAttributes(Context ctx, + float r, float g, + float b, float a, + boolean enableLight); + + /** + * This native method makes sure that the rendering for this canvas + * gets done now. + */ + abstract void syncRender(Context ctx, boolean wait); + + // The native method that sets this ctx to be the current one + abstract boolean useCtx(Context ctx, Drawable drawable); + + // Optionally release the context. A pipeline may override this and + // returns true if the context was released. + boolean releaseCtx(Context ctx) { + return false; + } + + abstract void clear(Context ctx, float r, float g, float b, boolean clearStencil); + + abstract void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, boolean useBiliearFilter); + + abstract void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, + float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, float mapZ, float alpha, boolean useBiliearFilter); + + abstract void executeRasterDepth(Context ctx, float posX, float posY, float posZ, + int srcOffsetX, int srcOffsetY, int rasterWidth, int rasterHeight, int depthWidth, int depthHeight, + int depthType, Object depthData); + + // The native method for setting the ModelView matrix. + abstract void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix); + + // The native method for setting the Projection matrix. + abstract void setProjectionMatrix(Context ctx, double[] projMatrix); + + abstract void resizeOffscreenLayer(Canvas3D cv, int width, int height); + + // The native method for setting the Viewport. + abstract void setViewport(Context ctx, int x, int y, int width, int height); + + // used for display Lists + abstract void newDisplayList(Context ctx, int displayListId); + abstract void endDisplayList(Context ctx); + abstract void callDisplayList(Context ctx, int id, boolean isNonUniformScale); + + abstract void freeDisplayList(Context ctx, int id); + abstract void freeTexture(Context ctx, int id); + + abstract int generateTexID(Context ctx); + abstract void texturemapping(Context 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); + + abstract boolean initTexturemapping(Context ctx, int texWidth, + int texHeight, int objectId); + + + // 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. + abstract void setRenderMode(Context ctx, int mode, boolean doubleBuffer); + + // Set glDepthMask. + abstract void setDepthBufferWriteEnable(Context ctx, boolean mode); + + + // --------------------------------------------------------------------- + + // + // Canvas3D / GraphicsConfigTemplate3D methods - logic dealing with + // native graphics configuration or drawing surface + // + + // Return a graphics config based on the one passed in. Note that we can + // assert that the input config is non-null and was created from a + // GraphicsConfigTemplate3D. + // This method must return a valid GraphicsConfig, or else it must throw + // an exception if one cannot be returned. + abstract GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig); + + // Get best graphics config from pipeline + abstract GraphicsConfiguration getBestConfiguration(GraphicsConfigTemplate3D gct, + GraphicsConfiguration[] gc); + + // Determine whether specified graphics config is supported by pipeline + abstract boolean isGraphicsConfigSupported(GraphicsConfigTemplate3D gct, + GraphicsConfiguration gc); + + // Methods to get actual capabilities from Canvas3D + abstract boolean hasDoubleBuffer(Canvas3D cv); + abstract boolean hasStereo(Canvas3D cv); + abstract int getStencilSize(Canvas3D cv); + abstract boolean hasSceneAntialiasingMultisample(Canvas3D cv); + abstract boolean hasSceneAntialiasingAccum(Canvas3D cv); + + // Methods to get native WS display and screen + abstract int getScreen(GraphicsDevice graphicsDevice); + + + // --------------------------------------------------------------------- + + // + // DrawingSurfaceObject methods + // + + // Method to construct a new DrawingSurfaceObject + abstract DrawingSurfaceObject createDrawingSurfaceObject(Canvas3D cv); + + // Method to free the drawing surface object + abstract void freeDrawingSurface(Canvas3D cv, DrawingSurfaceObject drawingSurfaceObject); + + // Method to free the native drawing surface object + abstract void freeDrawingSurfaceNative(Object o); + +} diff --git a/src/javax/media/j3d/PointArray.java b/src/javax/media/j3d/PointArray.java new file mode 100644 index 0000000..bfe6fae --- /dev/null +++ b/src/javax/media/j3d/PointArray.java @@ -0,0 +1,186 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + public PointArray(int vertexCount, int vertexFormat) { + super(vertexCount,vertexFormat); + + if (vertexCount < 1 ) + throw new IllegalArgumentException(J3dI18N.getString("PointArray0")); + } + + /** + * Constructs an empty PointArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty PointArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 1 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public PointArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + if (vertexCount < 1 ) + throw new IllegalArgumentException(J3dI18N.getString("PointArray0")); + } + + /** + * Creates the retained mode PointArrayRetained object that this + * PointArray object will point to. + */ + @Override + void createRetained() { + this.retained = new PointArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + PointArrayRetained rt = (PointArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + PointArray p = new PointArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes); + p.duplicateNodeComponent(this); + return p; + } +} diff --git a/src/javax/media/j3d/PointArrayRetained.java b/src/javax/media/j3d/PointArrayRetained.java new file mode 100644 index 0000000..77e2433 --- /dev/null +++ b/src/javax/media/j3d/PointArrayRetained.java @@ -0,0 +1,308 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * The PointArray object draws the array of vertices as individual points. + */ + +class PointArrayRetained extends GeometryArrayRetained { + + PointArrayRetained() { + this.geoType = GEO_TYPE_POINT_SET; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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); + Point3d pnt = new Point3d(); + int[] vtxIndexArr = new int[1]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < validVertexCount) { + vtxIndexArr[0] = i; + getVertexData(i++, pnt); + if (intersectPntAndRay(pnt, pickRay.origin, + pickRay.direction, sdist)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + vtxIndexArr[0] = i; + getVertexData(i++, pnt); + if (intersectPntAndRay(pnt, pickSegment.start, + dir, sdist) && + (sdist[0] <= 1.0)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + case PickShape.PICKBOUNDINGSPHERE: + case PickShape.PICKBOUNDINGPOLYTOPE: + Bounds bounds = ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + vtxIndexArr[0] = i; + getVertexData(i++, pnt); + if (bounds.intersect(pnt)) { + if (flags == 0) { + return true; + } + sdist[0] = pickShape.distance(pnt); + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < validVertexCount) { + vtxIndexArr[0] = i; + getVertexData(i++, pnt); + if (intersectCylinder(pnt, pickCylinder, sdist)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < validVertexCount) { + vtxIndexArr[0] = i; + getVertexData(i++, pnt); + if (intersectCone(pnt, pickCone, sdist)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = pnt.x; + y = pnt.y; + z = pnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + @Override + 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; + } + + + @Override + 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 + @Override + 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; + } + + @Override + int getClassType() { + return POINT_TYPE; + } +} diff --git a/src/javax/media/j3d/PointAttributes.java b/src/javax/media/j3d/PointAttributes.java new file mode 100644 index 0000000..2c5135a --- /dev/null +++ b/src/javax/media/j3d/PointAttributes.java @@ -0,0 +1,236 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The PointAttributes object defines all attributes that apply to + * point primitives. The point attributes that can be defined are:

+ *

    + *
  • Size - the size of the point, in pixels. The default is a point + * size of one pixel.
  • + *

  • Antialiasing - for points greater than one-pixel in size, + * antialiasing smooths the outline of the point when it is rendered.
  • + *

+ * If antialiasing is disabled (the default), fractional point sizes + * are rounded to integer sizes, and a screen-aligned square region + * of pixels is drawn.

+ *

+ * 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. + *

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SIZE_READ, + ALLOW_ANTIALIASING_READ + }; + + /** + * Constructs a PointAttributes object with default parameters. + * The default values are as follows: + *
    + * point size : 1
    + * point antialiasing : false
    + *
+ */ + public PointAttributes(){ + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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){ + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + *

+ * 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. + *

+ * @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. + */ + @Override + void createRetained() { + this.retained = new PointAttributesRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + PointAttributes pa = new PointAttributes(); + pa.duplicateNodeComponent(this); + return pa; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/PointAttributesRetained.java b/src/javax/media/j3d/PointAttributesRetained.java new file mode 100644 index 0000000..3025fa9 --- /dev/null +++ b/src/javax/media/j3d/PointAttributesRetained.java @@ -0,0 +1,221 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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(state); + 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 + */ + @Override + 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 + */ + void updateNative(Context ctx) { + Pipeline.getPipeline().updatePointAttributes(ctx, pointSize, pointAntialiasing); + } + + + /** + * Initializes a mirror object, point the mirror object to the retained + * object if the object is not editable + */ + @Override + synchronized void initMirrorObject() { + ((PointAttributesRetained)mirror).set(this); + } + + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + @Override + void handleFrequencyChange(int bit) { + if (bit == PointAttributes.ALLOW_SIZE_WRITE || + bit == PointAttributes.ALLOW_ANTIALIASING_WRITE) { + setFrequencyChangeMask(bit, 0x1); + } + } + +} diff --git a/src/javax/media/j3d/PointLight.java b/src/javax/media/j3d/PointLight.java new file mode 100644 index 0000000..67ee2a9 --- /dev/null +++ b/src/javax/media/j3d/PointLight.java @@ -0,0 +1,325 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Color3f; +import javax.vecmath.Point3f; + + +/** + * 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. + *

+ * 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. + *

+ * 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: + *

    + *
  • Constant attenuation
  • + *
  • Linear attenuation
  • + *
  • Quadratic attenuation
+ *

+ * A PointLight is attenuated by the reciprocal of the sum of: + *

+ *

    + * The constant attenuation factor
    + * The Linear attenuation factor times the distance between the light + * and the vertex being illuminated
    + * The quadratic attenuation factor times the square of the distance + * between the light and the vertex + *
+ *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_POSITION_READ, + ALLOW_ATTENUATION_READ + }; + + /** + * Constructs a PointLight node with default parameters. + * The default values are as follows: + *

    + * position : (0,0,0)
    + * attenuation : (1,0,0)
    + *
+ */ + public PointLight() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((PointLightRetained)this.retained).initPosition(position); + ((PointLightRetained)this.retained).initAttenuation(attenuation); + } + + /** + * Creates the retained mode PointLightRetained object that this + * PointLight component object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + PointLight p = new PointLight(); + p.duplicateNode(this, forceDuplicate); + return p; + } + + + /** + * Copies all PointLight information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/PointLightRetained.java b/src/javax/media/j3d/PointLightRetained.java new file mode 100644 index 0000000..4ea69d7 --- /dev/null +++ b/src/javax/media/j3d/PointLightRetained.java @@ -0,0 +1,339 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3f; + +/** + * 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((Bounds)null); + } + + /** + * 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. + */ + @Override + void update(Context ctx, int lightSlot, double scale) { + validateAttenuationInEc(scale); + Pipeline.getPipeline().updatePointLight(ctx, + lightSlot, color.x, color.y, color.z, + attenuation.x, linearAttenuationInEc, + quadraticAttenuationInEc, + xformPosition.x, xformPosition.y, + xformPosition.z); + } + + @Override + 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 + @Override + void doSetLive(SetLiveState s) { + super.setLive(s); + } + + @Override + 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 + @Override + 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 + @Override + 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 + @Override + void updateTransformChange() { + super.updateTransformChange(); + + Transform3D lastLocalToVworld = getLastLocalToVworld(); + + lastLocalToVworld.transform(position, xformPosition); + localToVworldScale = lastLocalToVworld.getDistanceScale(); + + validateAttenuationInEc(0.0861328125); + } + + @Override + void sendMessage(int attrMask, Object attr) { + + J3dMessage createMessage = new J3dMessage(); + 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); + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + xform.transform.transform(position, position); + } +} diff --git a/src/javax/media/j3d/PointSound.java b/src/javax/media/j3d/PointSound.java new file mode 100644 index 0000000..907bf5c --- /dev/null +++ b/src/javax/media/j3d/PointSound.java @@ -0,0 +1,580 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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. + *

+ * Distance Gain Attenuation + *

    + * 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. + *

    + * 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). + *

    + * getDistanceGainLength method returns the length of the distance gain + * attenuation arrays. Arrays passed into getDistanceGain methods should all + * be at least this size. + *

    + * There are two methods for getDistanceGain, one returning an array of + * points, the other returning separate arrays for each attenuation + * component.

+ */ + +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; + + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_POSITION_READ, + ALLOW_DISTANCE_GAIN_READ + }; + + /** + * Constructs and initializes a new PointSound node using default + * parameters. The following default values are used: + *
    + * position vector: (0.0, 0.0, 0.0)
    + * Back attenuation: null
    + * distance gain attenuation: null (no attenuation performed)
    + *
+ */ + public PointSound() { + // Uses default values defined for Sound and PointSound nodes + super(); + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + PointSound p = new PointSound(); + p.duplicateNode(this, forceDuplicate); + return p; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * PointSound + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + + /** + * Copies all PointSound information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/PointSoundRetained.java b/src/javax/media/j3d/PointSoundRetained.java new file mode 100644 index 0000000..14117a9 --- /dev/null +++ b/src/javax/media/j3d/PointSoundRetained.java @@ -0,0 +1,306 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point2f; +import javax.vecmath.Point3f; + +/** + * 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 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. + */ + @Override + 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]; + Object o = objs[4]; + if (o instanceof Point3f) { + 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 + @Override + 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 + ")"); + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + xform.transform.transform(position, position); + } +} diff --git a/src/javax/media/j3d/PolygonAttributes.java b/src/javax/media/j3d/PolygonAttributes.java new file mode 100644 index 0000000..4713806 --- /dev/null +++ b/src/javax/media/j3d/PolygonAttributes.java @@ -0,0 +1,494 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + *

    + *
  • Rasterization mode - defines how the polygon is drawn: as points, + * outlines, or filled.

    + *

      + *
    • POLYGON_POINT - the polygon is rendered as points + * drawn at the vertices.
    • + *

    • POLYGON_LINE - the polygon is rendered as lines + * drawn between consecutive vertices.
    • + *

    • POLYGON_FILL - the polygon is rendered by filling the interior + * between the vertices. The default mode.
    • + *

    + *
  • Face culling - defines which polygons are culled (discarded) + * before they are converted to screen coordinates.

    + *

      + *
    • CULL_NONE - disables face culling.
    • + *
    • CULL_BACK - culls all back-facing polygons. The default.
    • + *
    • CULL_FRONT - culls all front-facing polygons.
    • + *

    + *
  • 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.
  • + *

    + *

  • 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:
  • + *

      + *
    • Offset bias - the constant polygon offset that is added to + * the final device coordinate Z value of polygon primitives.
    • + *

      + *

    • 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.
    • + *

    + * These values can be either positive or negative. The default + * for both of these values is 0.0.

    + *

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_CULL_FACE_READ, + ALLOW_MODE_READ, + ALLOW_NORMAL_FLIP_READ, + ALLOW_OFFSET_READ + }; + + /** + * Constructs a PolygonAttributes object with default parameters. + * The default values are as follows: + *
    + * cull face : CULL_BACK
    + * back face normal flip : false
    + * polygon mode : POLYGON_FILL
    + * polygon offset : 0.0
    + * polygon offset factor : 0.0
    + *
+ */ + public PolygonAttributes() { + // Just use defaults for all attributes + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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")); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + void createRetained() { + this.retained = new PolygonAttributesRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + PolygonAttributes pga = new PolygonAttributes(); + pga.duplicateNodeComponent(this); + return pga; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + + @Override + 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/javax/media/j3d/PolygonAttributesRetained.java b/src/javax/media/j3d/PolygonAttributesRetained.java new file mode 100644 index 0000000..53f25e1 --- /dev/null +++ b/src/javax/media/j3d/PolygonAttributesRetained.java @@ -0,0 +1,360 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + */ + @Override + 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 + */ + void updateNative(Context ctx) { + Pipeline.getPipeline().updatePolygonAttributes(ctx, + polygonMode, cullFace, backFaceNormalFlip, + polygonOffset, polygonOffsetFactor); + } + + /** + * Initializes a mirror object, point the mirror object to the retained + * object if the object is not editable + */ + @Override + synchronized void initMirrorObject() { + ((PolygonAttributesRetained) mirror).set(this); + } + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + + } + @Override + 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/javax/media/j3d/PositionInterpolator.java b/src/javax/media/j3d/PositionInterpolator.java new file mode 100644 index 0000000..83ad045 --- /dev/null +++ b/src/javax/media/j3d/PositionInterpolator.java @@ -0,0 +1,220 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = 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 + * TransformInterpolator.setTransformAxis(Transform3D) + */ + public void setAxisOfTranslation(Transform3D axisOfTranslation) { + setTransformAxis(axisOfTranslation); + } + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.getTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + PositionInterpolator pi = new PositionInterpolator(); + pi.duplicateNode(this, forceDuplicate); + return pi; + } + + /** + * Copies all PositionInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + super.duplicateAttributes(originalNode, forceDuplicate); + + PositionInterpolator pi = (PositionInterpolator) originalNode; + + setStartPosition(pi.getStartPosition()); + setEndPosition(pi.getEndPosition()); + } +} diff --git a/src/javax/media/j3d/PositionPathInterpolator.java b/src/javax/media/j3d/PositionPathInterpolator.java new file mode 100644 index 0000000..f303b10 --- /dev/null +++ b/src/javax/media/j3d/PositionPathInterpolator.java @@ -0,0 +1,279 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = 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 + * TransformInterpolator.setTransformAxis(Transform3D) + */ + public void setAxisOfTranslation(Transform3D axisOfTranslation) { + setTransformAxis(axisOfTranslation); + } + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.getTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + PositionPathInterpolator ppi = new PositionPathInterpolator(); + ppi.duplicateNode(this, forceDuplicate); + return ppi; + } + + + /** + * Copies all PositionPathInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/QuadArray.java b/src/javax/media/j3d/QuadArray.java new file mode 100644 index 0000000..8f5ce57 --- /dev/null +++ b/src/javax/media/j3d/QuadArray.java @@ -0,0 +1,193 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 4 + * or vertexCount is not a multiple of 4 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 4 + * or vertexCount is not a multiple of 4 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty QuadArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 4 + * or vertexCount is not a multiple of 4 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public QuadArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + 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. + */ + @Override + void createRetained() { + this.retained = new QuadArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + QuadArrayRetained rt = (QuadArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + QuadArray q = new QuadArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes); + q.duplicateNodeComponent(this); + return q; + } + + +} diff --git a/src/javax/media/j3d/QuadArrayRetained.java b/src/javax/media/j3d/QuadArrayRetained.java new file mode 100644 index 0000000..b4d728d --- /dev/null +++ b/src/javax/media/j3d/QuadArrayRetained.java @@ -0,0 +1,525 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + + Point3d pnts[] = new Point3d[4]; + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + int[] vtxIndexArr = new int[4]; + + 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) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + while (i < validVertexCount) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<4; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + + } + + // intersect pnts[] with every quad in this object + @Override + 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; + } + + + @Override + 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 + @Override + 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. + @Override + void computeCentroid() { + int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ? + initialVertexIndex : initialCoordIndex); + + Point3d pnt0 = new Point3d(); + Point3d pnt1 = new Point3d(); + Point3d pnt2 = new Point3d(); + Point3d pnt3 = new Point3d(); + Vector3d vec = new Vector3d(); + Vector3d normal = new Vector3d(); + Vector3d tmpvec = new Vector3d(); + + 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; + } + } + + @Override + int getClassType() { + return QUAD_TYPE; + } +} diff --git a/src/javax/media/j3d/Raster.java b/src/javax/media/j3d/Raster.java new file mode 100644 index 0000000..9b2cb3c --- /dev/null +++ b/src/javax/media/j3d/Raster.java @@ -0,0 +1,822 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Dimension; +import java.awt.Point; + +import javax.vecmath.Point3f; + + +/** + * 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. + *

+ * 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; + + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_POSITION_READ, + ALLOW_OFFSET_READ, + ALLOW_IMAGE_READ, + ALLOW_DEPTH_COMPONENT_READ, + ALLOW_SIZE_READ, + ALLOW_TYPE_READ, + ALLOW_CLIP_MODE_READ + }; + + /** + * Constructs a Raster object with default parameters. + * The default values are as follows: + *

    + * type : RASTER_COLOR
    + * clipMode : CLIP_POSITION
    + * position : (0,0,0)
    + * srcOffset : (0,0)
    + * size : (0,0)
    + * dstOffset : (0,0)
    + * image : null
    + * depth component : null
    + *
+ */ + public Raster() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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 + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is ImageClass.NIO_IMAGE_BUFFER. + */ + public Raster(Point3f pos, + int type, + int xSrcOffset, + int ySrcOffset, + int width, + int height, + ImageComponent2D image, + DepthComponent depthComponent) { + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is ImageClass.NIO_IMAGE_BUFFER. + */ + public Raster(Point3f pos, + int type, + Point srcOffset, + Dimension size, + ImageComponent2D image, + DepthComponent depthComponent) { + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is ImageClass.NIO_IMAGE_BUFFER. + * + * @since Java 3D 1.3 + */ + public Raster(Point3f pos, + int type, + int clipMode, + Point srcOffset, + Dimension size, + Point dstOffset, + ImageComponent2D image, + DepthComponent depthComponent) { + + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 + * setSrcOffset(int,int) + */ + public void setOffset(int xSrcOffset, int ySrcOffset) { + setSrcOffset(xSrcOffset, ySrcOffset); + } + + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * setSrcOffset(java.awt.Point) + */ + public void setOffset(Point srcOffset) { + setSrcOffset(srcOffset); + } + + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * getSrcOffset(java.awt.Point) + */ + 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 + * + * @exception IllegalSharingException if this Raster is live and + * the specified image is being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalArgumentException if the image class of the specified + * ImageComponent2D is ImageClass.NIO_IMAGE_BUFFER. + * + */ + public void setImage(ImageComponent2D image) { + + if (isLiveOrCompiled()) + if(!this.getCapability(ALLOW_IMAGE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("Raster3")); + + // Do illegal sharing check + if(image != null) { + ImageComponent2DRetained imageRetained = (ImageComponent2DRetained) image.retained; + if(imageRetained.getUsedByOffScreen()) { + if(isLive()) { + throw new IllegalSharingException(J3dI18N.getString("Raster12")); + } + } + } + + ((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) + */ + @Override + public NodeComponent cloneNodeComponent() { + Raster r = new Raster(); + r.duplicateNodeComponent(this); + return r; + } + + + /** + * 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) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } + + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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. + */ + @Override + 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/javax/media/j3d/RasterRetained.java b/src/javax/media/j3d/RasterRetained.java new file mode 100644 index 0000000..dd8245e --- /dev/null +++ b/src/javax/media/j3d/RasterRetained.java @@ -0,0 +1,691 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Dimension; +import java.awt.Point; +import java.util.ArrayList; + +import javax.vecmath.Point2d; +import javax.vecmath.Point2i; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Point4d; + +/** + * A Retained Raster. + */ + +class RasterRetained extends GeometryRetained { + + /** + * Raster type + */ + int type = Raster.RASTER_COLOR; + + private int clipMode = Raster.CLIP_POSITION; + private Point3f position = new Point3f(); + private int xSrcOffset = 0; + private int ySrcOffset = 0; + private int width = 0; + private int height = 0; + private int xDstOffset = 0; + private int yDstOffset = 0; + ImageComponent2DRetained image = null; + Texture2DRetained texture = null; + DepthComponentRetained depthComponent = null; + + RasterRetained() { + this.geoType = GEO_TYPE_RASTER; + } + + /** + * 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(); + Shape3DRetained ms, shape; + int sn; + for(int i = 0; i < un; i++) { + ArrayList shapeList = userLists.get(i); + sn = shapeList.size(); + for(int j = 0; j < sn; j++) { + ms = 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(); + } + + /** + * Gets 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); + } + + + /** + * Initializes the raster image to the specified image. + * @param image new ImageCompoent2D object used as the raster image + */ + final void initImage(ImageComponent2D img) { + + int texFormat; + + if(img == null) { + image = null; + texture = null; + return; + } + + image = (ImageComponent2DRetained) img.retained; + image.setEnforceNonPowerOfTwoSupport(true); + switch(image.getNumberOfComponents()) { + case 1: + texFormat = Texture.INTENSITY; + break; + case 2: + texFormat = Texture.LUMINANCE_ALPHA; + break; + case 3: + texFormat = Texture.RGB; + break; + case 4: + texFormat = Texture.RGBA; + break; + default: + assert false; + return; + } + + Texture2D tex2D = new Texture2D(Texture.BASE_LEVEL, texFormat, + img.getWidth(), img.getHeight()); + texture = (Texture2DRetained) tex2D.retained; + texture.setUseAsRaster(true); + // Fix to issue 372 : ImageComponent.set(BufferedImage) ignored when used by Raster + image.addUser(texture); + texture.initImage(0,img); + + } + + /** + * 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 img) { + + if((img != null) && + (img.getImageClass() == ImageComponent.ImageClass.NIO_IMAGE_BUFFER)) { + throw new IllegalArgumentException(J3dI18N.getString("Background14")); + } + + TextureRetained oldTex = this.texture; + if (source.isLive()) { + if (this.texture != null) { + this.texture.clearLive(refCount); + } + } + + // Issue 370: only hold the geomLock while calling initImage + // (cannot hold it while sending a message). + geomLock.getLock(); + initImage(img); + geomLock.unLock(); + + if (source.isLive()) { + if (texture != null) { + texture.setLive(inBackgroundGroup, refCount); + } + + sendChangedMessage((J3dThread.UPDATE_RENDER|J3dThread.UPDATE_RENDERING_ATTRIBUTES), + oldTex, this.texture); + } + } + + /** + * 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) { + geomLock.getLock(); + if (this.source.isLive()) { + if (this.depthComponent != null) { + this.depthComponent.clearLive(refCount); + } + if (depthComponent != null) { + ((DepthComponentRetained)depthComponent.retained).setLive(inBackgroundGroup, refCount); + } + } + + 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); + } + + @Override + void setLive(boolean inBackgroundGroup, int refCount) { + super.doSetLive(inBackgroundGroup, refCount); + if (texture != null) { + texture.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(); + } + + @Override + void clearLive(int refCount) { + super.clearLive(refCount); + if (texture != null) + texture.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); + } + */ + + @Override + 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); + } + } + + @Override + 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 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 + srcOffset.x = (int)-winCoord.x+1; + winCoord.x = 1; + } + + if(winCoord.y < 1) { + // Negate the window position and use this as the offset + srcOffset.y = (int)-winCoord.y+1; + winCoord.y = 1; + } + + //check if user-specified subimage is smaller than the clipped image + if (srcOffset.x < xSrcOffset) + srcOffset.x = xSrcOffset; + if(srcOffset.y < ySrcOffset) + srcOffset.y = ySrcOffset; + + } + + + private boolean isRasterClipPositionInside(Point3d clipCoord) { + return (clipCoord.x >= -1.0) && (clipCoord.x <= 1.0) && + (clipCoord.y >= -1.0) && (clipCoord.y <= 1.0); + } + + private void computeObjCoord(Canvas3D canvas, Point2d winCoord, Point3d objCoord, + Transform3D localToImagePlate) { + // 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) + localToImagePlate.invert(); + localToImagePlate.transform(objCoord); + } + + private Point3d computeWinCoord(Canvas3D canvas, RenderAtom ra, + Point2d winCoord, Point3d objCoord, + Transform3D localToImagePlate) { + // 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. + return null; + } + + // 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]]; + + + Point3d clipCoord3 = new Point3d(); + clipCoord3.set(objCoord); + Point4d clipCoord4 = new Point4d(); + + // Transform point from local coord. to clipping coord. + lvw.transform(clipCoord3); + canvas.vworldToEc.transform(clipCoord3); + canvas.projTrans.transform(clipCoord3, clipCoord4); + + // clip check in Z + if((clipCoord4.w <= 0.0) || + (clipCoord4.z > clipCoord4.w) || (-clipCoord4.z > clipCoord4.w)) { + + return null; + } + double invW = 1.0 / clipCoord4.w; + + clipCoord3.x = clipCoord4.x * invW; + clipCoord3.y = clipCoord4.y * invW; + clipCoord3.z = clipCoord4.z * invW; + + // Get Vworld to image plate Xform + canvas.getLastVworldToImagePlate(localToImagePlate); + + // 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 + localToImagePlate.mul(lvw); + + // Transform the Raster's position from object to world coordinates + localToImagePlate.transform(objCoord); + + + // Get the window coordinates of this point + canvas.getPixelLocationFromImagePlate(objCoord, winCoord); + + return clipCoord3; + } + + @Override + 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) { + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + return false; + } + + @Override + boolean intersect(Bounds targetBound) { + return false; + } + + @Override + boolean intersect(Point3d[] pnts) { + return false; + } + @Override + boolean intersect(Transform3D thisToOtherVworld, GeometryRetained + geom) { + return false; + } + @Override + boolean intersect(Transform3D thisLocalToVworld, + Transform3D otherLocalToVworld, + GeometryRetained geom) { + return false; + } + + @Override + boolean intersect(Transform3D thisLocalToVworld, Bounds targetBound) { + return false; + } + @Override + void handleFrequencyChange(int bit) { + if (bit == Raster.ALLOW_IMAGE_WRITE) + setFrequencyChangeMask(bit, 0x1); + + } + +} diff --git a/src/javax/media/j3d/RenderAtom.java b/src/javax/media/j3d/RenderAtom.java new file mode 100644 index 0000000..1d1bb8d --- /dev/null +++ b/src/javax/media/j3d/RenderAtom.java @@ -0,0 +1,369 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * 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; + + 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 !TransparencyAttributesRetained.useAlpha(ta); + } + + 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); + } + + @Override + 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/javax/media/j3d/RenderAtomListInfo.java b/src/javax/media/j3d/RenderAtomListInfo.java new file mode 100644 index 0000000..aaddcd3 --- /dev/null +++ b/src/javax/media/j3d/RenderAtomListInfo.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 { + +final RenderAtom renderAtom; // RenderAtom that its a part of + +// Specific geometry index in the GeometryAtom geometryArr list that +// corresponds to this RenderAtomListInfo +final 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; + +RenderAtomListInfo(RenderAtom ra, int idx) { + renderAtom = ra; + index = idx; +} + + GeometryRetained geometry() { + return renderAtom.geometryAtom.geometryArray[index]; + } +} diff --git a/src/javax/media/j3d/RenderBin.java b/src/javax/media/j3d/RenderBin.java new file mode 100644 index 0000000..aacef06 --- /dev/null +++ b/src/javax/media/j3d/RenderBin.java @@ -0,0 +1,7066 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; + +import javax.vecmath.Color3f; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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); + + // 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 ShaderBin that are soleUser that needs to have its components updated @updateObject + * time + */ +ArrayList sBinUpdateList = 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); + + /** + * 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; + + private Comparator transparencySortComparator = null; + +private ArrayList toBeAddedTextureResourceFreeList = new ArrayList(5); +private ArrayList displayListResourceFreeList = new ArrayList(5); + +// 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; + + + private boolean reEvaluateBg = true; + private boolean reloadBgTexture = 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) +// Fix for Issue 5: change this to a Set rather than a list to +// avoid duplicates entried +Collection sharedDList = new HashSet(); + +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); + +// 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 +private HashSet addDlist = new HashSet(); +private HashSet removeDlist = new HashSet(); + +// 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 +private LinkedHashSet dirtyDepthSortRenderAtom = new LinkedHashSet(); + private 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 + */ + @Override + public void updateObject() { + int i, j, k; + RenderAtomListInfo ra; + LightBin tmp; + ObjectUpdate ob; + OrderedBin orderBin; + int size; + + // System.err.println("dirtyRenderMoleculeList.size = "+dirtyRenderMoleculeList.size()); + // System.err.println("reEvaluateBg = "+reEvaluateBg); + // System.err.println("reEvaluateClip = "+reEvaluateClip); + // System.err.println("<========+End All Cached Values===========>"); + // Add the new lightBins that have been created + // System.err.println("objUpdateList.size = "+objUpdateList.size()); + // System.err.println("addOpaqueBin = "+addOpaqueBin); + // System.err.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 0) { + for (i = 0 ; i < size; i++) { + orderBin = obList.get(i); + orderBin.addRemoveOrderedCollection(); + } + } + + size = ogCIOList.size(); + if(size > 0) { + for(i=0; i 0 ) { + + for (i = 0; i < size; i++) { + ArrayList obs = orderedBinsList.get(i); + ArrayList list = 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 = raLocaleVwcBoundsUpdateList.get(i); + renderAtom.updateLocaleVwcBounds(); + } + } + + if ((size = aBinUpdateList.size()) > 0) { + for (i = 0; i < size; i++) { + AttributeBin abin = aBinUpdateList.get(i); + abin.updateNodeComponent(); + } + } + + if ((size = sBinUpdateList.size()) > 0) { + for (i = 0; i < size; i++) { + ShaderBin sbin = sBinUpdateList.get(i); + sbin.updateNodeComponent(); + } + } + + // Update the sole user TextureBins. + if (tbUpdateList.size() > 0) { + TextureBin tb; + size = tbUpdateList.size(); + for (i = 0; i < size; i++) { + tb = 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 = tbUpdateList.get(i); + // Bug Id : 4701430 - Have to be sure tb.shaderBin is + // not equal to null. This is a temporary fix for j3d1.3. + if (((tb.tbFlag & TextureBin.RESORT) != 0) && + (tb.shaderBin != null)) { + + tb.shaderBin.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++) { + RenderMolecule rm = 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++) { + rmUpdateList.get(i).reEvaluateEquivalence(); + } + } + + + + size = objUpdateList.size(); + if ( size > 0) { + for (i = 0; i < size; i++) { + ob = objUpdateList.get(i); + ob.updateObject(); + } + } + + size = dirtyReferenceGeomList.size(); + if ( size > 0) { + GeometryArrayRetained geo; + Canvas3D canvases[] = view.getCanvases(); + + for (i = 0; i < size; i++) { + geo = 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 ((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.err.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 0) { +//System.err.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); + if (nc.isByReference()) { + nc.geomLock.getLock(); + for (j = 0; j 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); + if (nc.isByReference()) { + nc.geomLock.getLock(); + for (j = 0; j 1); + + // renderBin is ready now, so send the offScreen message + size = offScreenMessage.size(); + if ( size > 0) { + for (i=size-1; i>=0; i--) { + J3dMessage m = 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 = new ArrayList(dirtyOrientedRAs); + } + 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.err.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; + renderAtom.parentTInfo[k].geometryAtom = renderAtom.geometryAtom; + } + } + + // 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.err.println("sortAll 3, size = "+size); + sortAll = true; + } + + if (size > 0) { + TransparentRenderingInfo dirtyList = null; + Iterator dirtyDepthSortIterator = dirtyDepthSortRenderAtom.iterator(); + while (dirtyDepthSortIterator.hasNext()) { + RenderAtom renderAtom = dirtyDepthSortIterator.next(); + if (!renderAtom.inRenderBin()) + continue; + renderAtom.dirtyMask &= ~RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST; + if (!sortAll) { + dirtyList = collectDirtyTRInfo(dirtyList, renderAtom); + } + } + + if (dirtyList != null) { + // System.err.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); + } + } + + // Remove entries that are found on both the add and remove lists + if (addDlist.size() > 0 && removeDlist.size() > 0) { + RenderAtomListInfo arr[] = new RenderAtomListInfo[addDlist.size()]; + arr = addDlist.toArray(arr); + for (i = 0; i < arr.length; i++) { + if (removeDlist.contains(arr[i])) { + addDlist.remove(arr[i]); + removeDlist.remove(arr[i]); + } + } + } + + 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 = displayListResourceFreeList.get(i); + VirtualUniverse.mc.freeDisplayListId(id); + } + + // lock list of dlist + // XXXX: Instead of copying could we keep 2 arrays + // and just toggle? + size = dirtyRenderMoleculeList.size(); + for (i = 0; i < size; i++) { + RenderMolecule rm = 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 = addDlistPerRinfo.get(i); + if (ra.geometry() != null) { + dlistLockList.add(ra.geometry()); + } + } + + } + + clearAllUpdateObjectState(); + /* + if (opaqueBin != null) { + System.err.println(this + "***** Begin Dumping OpaqueBin *****"); + dumpBin(opaqueBin); + System.err.println("***** End Dumping OpaqueBin *****"); + } + */ + + } + + + // Shared context case + void updateDlistRendererResource(Renderer rdr) { + int i; + int size = 0; + RenderAtomListInfo arr[]; + RenderAtomListInfo ra; + + // XXXX: there is a possible problem in the case of multiple + // renderers (i.e., multiple screens). Unless the + // MasterControl sends us a separate message for each + // renderer, we won't create a new display list for renderers + // other than the one passed into this method. + + if (rdr == null) { + return; + } + + if ((size = addDlist.size()) > 0) { + arr = new RenderAtomListInfo[size]; + arr = addDlist.toArray(arr); + 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.addDlistUser(this, ra); + + 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 = new RenderAtomListInfo[size]; + arr = removeDlist.toArray(arr); + for (i = 0; i < size; i++) { + ra = arr[i]; + sharedDList.remove(ra); + + GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry(); + geo.removeDlistUser(this, ra); + // System.err.println("========> geo.refcount = "+geo.refCount); + // add this geometry's dlist to be freed + if (geo.isDlistUserSetEmpty(this)) { + 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 = dirtyList.get(i); + GeometryArrayRetained geo = (GeometryArrayRetained)ra.geometry(); + if ( (geo.resourceCreationMask & rdr.rendererBit) != 0) { + rdr.dirtyRenderAtomList.add(ra); + } + } + rdr.dirtyDisplayList = true; + dirtyList.clear(); + } + } + + // Non-shared context case + void updateDlistCanvasResource(Canvas3D[] canvases) { + int i, j; + Canvas3D cv; + int size = 0; + RenderAtomListInfo arr[]; + RenderAtomListInfo ra; + + // Add the newly added dlist to the sharedList + if ((size = addDlist.size()) > 0) { + arr = new RenderAtomListInfo[size]; + arr = addDlist.toArray(arr); + for (i = 0; i 0) { + arr = new RenderAtomListInfo[size]; + arr = removeDlist.toArray(arr); + for (i = 0; i < size; i++) { + sharedDList.remove(arr[i]); + // Fix for Issue 5: remove this render atom from the list of users + // of its geometry for this RenderBin + GeometryArrayRetained geo = (GeometryArrayRetained) arr[i].geometry(); + geo.removeDlistUser(this, 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 = new RenderAtomListInfo[size]; + arr = addDlist.toArray(arr); + for (i = 0; i 0) { + arr = new RenderAtomListInfo[size]; + arr = removeDlist.toArray(arr); + for (i = 0; i < size; i++) { + GeometryArrayRetained geo = + (GeometryArrayRetained) arr[i].geometry(); + + // add this geometry's dlist to be freed + if (geo.isDlistUserSetEmpty(this)) { + if (cv.ctx != null) { + 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 0) { + for (int j = 0; j < size; j++) { + RenderAtomListInfo rinfo = 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 = 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 = 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; + } + } + } + + // Take care of display list that should be freed + size = displayListResourceFreeList.size(); + Integer displayListIDObj; + + for (int j=0; j 0) { + for ( j = 0; j < size; j++) { + RenderAtomListInfo rinfo = 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 = 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 = 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; + } + } + // 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 = removeDlistPerRinfo.get(j); + cv.displayListResourceFreeList.add(new Integer(ra.renderAtom.dlistIds[ra.index])); + ra.groupType = 0; + ra.renderAtom.dlistIds[ra.index] = -1; + + } + } + + } + + @Override + void processMessages(long referenceTime) { + int i; + 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; + reloadBgTexture = 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.SHADER_APPEARANCE_CHANGED: + case J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED: + case J3dMessage.SHADER_ATTRIBUTE_CHANGED: + processShaderComponentChanged(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; + // XXXX : 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(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.err.println("viewFrustumBBox = " + this); + + ViewPlatform vp = view.getViewPlatform(); + if (vp != null) { + allEnComp = universe.geometryStructure. + getVisibleBHTrees(this, 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(); + for (int k = 0; k < size; k++) { + updateCheckList.get(k).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; + + 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= 0) { + + // Issue 471 - Don't check ATTRIBUTE_VALUE_UPDATE, there is no need + // to do anything to the shader bins when a value changes. + boolean spUpdate = + ((component & ShaderAppearanceRetained.SHADER_PROGRAM) != 0); + boolean sasUpdate = + (((component & ShaderAppearanceRetained.SHADER_ATTRIBUTE_SET) != 0) || + ((component & ShaderConstants.ATTRIBUTE_SET_PUT) != 0) || + ((component & ShaderConstants.ATTRIBUTE_SET_REMOVE) != 0) || + ((component & ShaderConstants.ATTRIBUTE_SET_CLEAR) != 0)); + + if (spUpdate) { + /* TODO : JADA - Sole user logic is incomplete. Will disable for JavaOne */ + //if (false && (sApp.mirror.changedFrequent & component) != 0) { + if(false) { + /* + System.err.println("RenderBin : Shader sole user (SHADER_PROGRAM)" + + ra.renderMolecule.textureBin.shaderBin); + */ + + ShaderBin sBin; + + for (i = start; i < gaArr.length; i++) { + ra = gaArr[i].getRenderAtom(view); + if (ra== null || !ra.inRenderBin()) + continue; + + sBin = ra.renderMolecule.textureBin.shaderBin; + + if (sBin.componentDirty == 0) { + sBinUpdateList.add(sBin); + sBin.componentDirty |= ShaderBin.SHADER_PROGRAM_DIRTY; + } + } + } else { + /* + System.err.println("RenderBin : not soleUser (SHADER_PROGRAM)" + + ra.renderMolecule.textureBin.shaderBin); + */ + + for (i = 0; i < gaArr.length; i++) { + ra = gaArr[i].getRenderAtom(view); + if (ra== null || !ra.inRenderBin()) + continue; + + AttributeBin attrBin = ra.renderMolecule.textureBin.attributeBin; + ra.renderMolecule.removeRenderAtom(ra); + reInsertShaderBin(attrBin, ra); + } + } + } else if (sasUpdate) { + /* TODO : JADA - Sole user logic is incomplete. Will disable for JavaOne */ + //if (false && (sApp.mirror.changedFrequent & component) != 0) { + if(false) { + /* + System.err.println("RenderBin : sole user (SHADER_ATTRIBUTE_SET)" + + ra.renderMolecule.textureBin.shaderBin); + */ + + ShaderBin sBin; + + for (i = 0; i < gaArr.length; i++) { + ra = gaArr[i].getRenderAtom(view); + if (ra== null || !ra.inRenderBin()) + continue; + + + sBin = ra.renderMolecule.textureBin.shaderBin; + + if (sBin.componentDirty == 0) { + sBinUpdateList.add(sBin); + sBin.componentDirty |= ShaderBin.SHADER_ATTRIBUTE_SET_DIRTY; + } + } + } else { + /* + System.err.println("RenderBin :not soleUser (SHADER_ATTRIBUTE_SET) " + + ra.renderMolecule.textureBin.shaderBin); + */ + + for (i = 0; i < gaArr.length; i++) { + ra = gaArr[i].getRenderAtom(view); + if (ra== null || !ra.inRenderBin()) + continue; + + AttributeBin attrBin = ra.renderMolecule.textureBin.attributeBin; + ra.renderMolecule.removeRenderAtom(ra); + reInsertShaderBin(attrBin, 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]; + 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.mirror.changedFrequent & TEXTURE_STATE_CHANGED) != 0) && + ((ra.renderMolecule.textureBin.tbFlag & + TextureBin.SOLE_USER) != 0)) { + +/* +System.err.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.err.println("renderbin. texture state changed tb not sole user " + + ra.renderMolecule.textureBin + " tb.tbFlag= " + + ra.renderMolecule.textureBin.tbFlag); + +System.err.println("......tb.soleUser= " + + ((ra.renderMolecule.textureBin.tbFlag & TextureBin.SOLE_USER) != 0) + + " app.mirror.changedFrequent= " + + ((app.mirror.changedFrequent & TEXTURE_STATE_CHANGED) != 0)); + +*/ + + for (i = start; i < gaArr.length; i++) { + ra = gaArr[i].getRenderAtom(view); + if (ra== null || !ra.inRenderBin()) + continue; + ShaderBin sb = ra.renderMolecule.textureBin.shaderBin; + ra.renderMolecule.removeRenderAtom(ra); + reInsertTextureBin(sb, 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.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.err.println("AppearanceRetained.POINT = "+AppearanceRetained.POINT); + // System.err.println("(app.mirror.changedFrequent & component) != 0 "+app.mirror.changedFrequent ); + // System.err.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; + + // XXXX: 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; + EnvironmentSet e; + 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.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.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.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) { + RenderAtom ra; + int i; + GeometryRetained geo; + GeometryAtom ga; + + for (i=0; i 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.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.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.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) { + 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 = 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]; + Texture2DRetained oldTex = (Texture2DRetained) objs[0]; + Texture2DRetained newTex = (Texture2DRetained) objs[1]; + + RasterRetained geo = (RasterRetained)ra.geometry(); + if (oldTex != null) { + addTextureResourceFreeList(oldTex); + ImageComponentRetained oldImage = oldTex.images[0][0]; + if (oldImage != null) { + removeNodeComponent(oldImage); + } + } + if (newTex != null) { + ImageComponentRetained newImage = newTex.images[0][0]; + if (newImage != null) { + addNodeComponent(newImage); + } + } + } + + } + + } + +void addTextureBin(TextureBin tb) { + textureBinList.add(tb); +} + +void removeTextureBin(TextureBin tb) { + textureBinList.remove(tb); +} + + void addDirtyRenderMolecule(RenderMolecule rm) { + + 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) { + 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; + Context ctx; + long 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 = rmList.get(i); + rm.updateDisplayList(cv); + } + rmList.clear(); + } + + size = dlistPerRinfoList.size(); + + if (size > 0) { + for (i = size-1; i >= 0 ; i--) { + Object[] obj = 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 = raList.get(i); + geo = (GeometryArrayRetained) ra.geometry(); + geo.resourceCreationMask &= ~bitMask; + } + + for (i = size-1; i >= 0; i--) { + ra = 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) { + + 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[] = new RenderAtomListInfo[size]; + arr = sharedDList.toArray(arr); + 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[] = new RenderAtomListInfo[size]; + arr = sharedDList.toArray(arr); + 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); + } + } + + private 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 adding to the dirty list .., transpSortMode = "+transpSortMode); + if (dirtyDepthSortRenderAtom.add(ra)) { + numDirtyTinfo += ra.rListInfo.length; + } + /* + else { + System.err.println("processTransformChanged: attempt to add RenderAtom already in dirty list"); + } + */ + ra.dirtyMask |= RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST; + + } + 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 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 ocs; + ArrayList parentChildOrderedBins; + OrderedBin parentOrderedBin; + int parentOrderedChildId; + + // 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 list = new ArrayList(5); + list.add(ob); + toBeAddedBinList.add(list); + } + else { + ArrayList list = 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 = ob.setOCForOI.get(n).intValue(); + if (oi == val) { + oc = 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 = ob.setOCForCI.get(n).intValue(); + if (val == ci) { + + oc = ob.valueOfSetOCForCI.get(n); + if (oc == null) { + oc = new OrderedCollection(); + ob.valueOfSetOCForCI.set(n, oc); + } + + break; + } + } + if (n == ob.setOCForCI.size()) { + oc = 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); + } + + private void removeOrderedHeadLightBin(LightBin lightBin) { + OrderedCollection oc; + + 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. + */ + private EnvironmentSet getEnvironmentSet(RenderAtom ra, LightRetained[] lights, + FogRetained fog, ModelClipRetained modelClip) { + EnvironmentSet envSet; + + envSet = new EnvironmentSet(ra, lights, fog, modelClip, this); + return (envSet); + } + + /** + * This finds or creates an AttributeBin for a given RenderAtom. + */ + private 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 = 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 an ShaderBin for a given RenderAtom. + */ + private ShaderBin findShaderBin(AttributeBin attributeBin, RenderAtom ra) { + int i, size; + ShaderBin currentBin; + ShaderAppearanceRetained sApp; + + if((ra != null) && (ra.app instanceof ShaderAppearanceRetained)) + sApp = (ShaderAppearanceRetained)ra.app; + else + sApp = null; + + currentBin = attributeBin.shaderBinList; + while (currentBin != null) { + if (currentBin.equals(sApp)) { + return currentBin; + } + currentBin = currentBin.next; + } + + // Check the "to-be-added" list of shaderBins for a match + size = attributeBin.addShaderBins.size(); + for (i = 0; i < size; i++) { + currentBin = attributeBin.addShaderBins.get(i); + if (currentBin.equals(sApp)) { + return currentBin; + } + } + + currentBin = getShaderBin(sApp); + attributeBin.addShaderBin(currentBin, this, sApp); + return currentBin; + } + + /** + * This finds or creates a TextureBin for a given RenderAtom. + */ + private TextureBin findTextureBin(ShaderBin shaderBin, RenderAtom ra) { + int i, size; + TextureBin currentBin; + TextureUnitStateRetained texUnitState[]; + + if (ra.app == null) { + texUnitState = null; + } else { + texUnitState = ra.app.texUnitState; + } + + currentBin = shaderBin.textureBinList; + while (currentBin != null) { + if (currentBin.equals(texUnitState, ra)) { + //System.err.println("1: Equal"); + return(currentBin); + } + currentBin = currentBin.next; + } + // Check the "to-be-added" list of TextureBins for a match + size = shaderBin.addTextureBins.size(); + for (i = 0; i < size; i++) { + currentBin = shaderBin.addTextureBins.get(i); + if (currentBin.equals(texUnitState, ra)) { + //System.err.println("2: Equal"); + return(currentBin); + } + } + // get a new texture bin for this texture unit state + currentBin = getTextureBin(texUnitState, ra.app); + shaderBin.addTextureBin(currentBin, this, ra); + return(currentBin); + } + + /** + * This finds or creates a RenderMolecule for a given RenderAtom. + */ + private RenderMolecule findRenderMolecule(TextureBin textureBin, + RenderAtom ra) { + + RenderMolecule currentBin; + PolygonAttributesRetained polygonAttributes; + LineAttributesRetained lineAttributes; + PointAttributesRetained pointAttributes; + MaterialRetained material; + ColoringAttributesRetained coloringAttributes; + TransparencyAttributesRetained transparencyAttributes; + int i; + TextureUnitStateRetained texUnitState[]; + RenderingAttributesRetained renderingAttributes; + + 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 + HashMap rmap = null; + HashMap> addmap = null; + if (ra.isOpaque()) { + rmap = textureBin.opaqueRenderMoleculeMap; + addmap = textureBin.addOpaqueRMs; + } + else { + rmap = textureBin.transparentRenderMoleculeMap; + addmap = textureBin.addTransparentRMs; + } + currentBin = 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.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 + ArrayList list = addmap.get(ra.geometryAtom.source.localToVworld[0]); + if (list != null) { + for (i = 0; i < list.size(); i++) { + currentBin = 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 ShaderBin. It creates one if there are none + * on the freelist. + */ + private ShaderBin getShaderBin(ShaderAppearanceRetained sApp) { + return new ShaderBin( sApp, this); + } + + /** + * This gets a new AttributeBin. It creates one if there are none + * on the freelist. + */ + private AttributeBin getAttributeBin(AppearanceRetained app, RenderingAttributesRetained ra) { + return new AttributeBin(app, ra, this); + } + + /** + * This gets a new LightBin. It creates one if there are none + * on the freelist. + */ + private LightBin getLightBin(int maxLights, BackgroundRetained bg, boolean inOpaque) { + LightBin lightBin; + + 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. + */ + private TextureBin getTextureBin(TextureUnitStateRetained texUnitState[], + AppearanceRetained app) { + return new TextureBin(texUnitState, app, this); + } + + /** + * This gets a new RenderMolecule. It creates one if there are none + * on the freelist. + */ + private RenderMolecule getRenderMolecule(GeometryAtom ga, + PolygonAttributesRetained polya, + LineAttributesRetained linea, + PointAttributesRetained pointa, + MaterialRetained material, + ColoringAttributesRetained cola, + TransparencyAttributesRetained transa, + RenderingAttributesRetained ra, + TextureUnitStateRetained[] texUnits, + Transform3D[] transform, + int[] transformIndex) { + + return new RenderMolecule(ga, polya, linea, pointa, + material, cola, transa, ra, + texUnits, + transform, transformIndex, this); + } + + + /** + * This finds or creates an EnviornmentSet for a given RenderAtom. + * This also deals with empty LightBin lists. + */ + private 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 = 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 = 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; + } + 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.err.println("========> renderOpaque"); + while (currentBin != null) { + //System.err.println("====> rendering Opaque Bin "); + currentBin.render(cv); + currentBin = currentBin.next; + } + + } + + /** + * This renders the transparent objects + */ + void renderTransparent(Canvas3D cv) { + boolean savedDepthBufferWriteEnable = true; + + //System.err.println("====> renderTransparent"); + TransparentRenderingInfo tinfo = transparentInfo; + if (tinfo != null) { + //System.err.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.err.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, 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.systemStencilAvailable; + int size = orderedBin.orderedCollections.size(); + + // System.err.println("RB : orderedBin.orderedCollections.size() " + size); + for (i=0; i fogs, boolean updateDirty, + boolean altAppDirty) { + EnvironmentSet e; + FogRetained newfog; + int i; + AppearanceRetained app; + Object[] retVal; + + int sz = renderAtoms.size(); + for (i = 0; i < sz; i++) { + RenderAtom ra = 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 = 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; + ModelClipRetained newModelClip; + int i; + AppearanceRetained app; + Object[] retVal; + int sz = renderAtoms.size(); + for (i = 0; i < sz; i++) { + RenderAtom ra = 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 = 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; + LightRetained[] lights; + int i; + AppearanceRetained app; + Object[] retVal; + int sz = renderAtoms.size(); + for (i = 0; i < sz; i++) { + RenderAtom ra = 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 = 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 = 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() { + int size; + + + // Vertex array is locked for every time renderer is run + size = lockGeometryList.size(); + for (int i = 0; i < size; i++) { + GeometryRetained geo = lockGeometryList.get(i); + geo.geomLock.getLock(); + } + + // dlist is locked only when they are rebuilt + size = dlistLockList.size(); + for (int i = 0; i < size ; i++) { + GeometryRetained geo = (GeometryRetained) dlistLockList.get(i); + geo.geomLock.getLock(); + + } + + // Lock all the by reference image components + size = nodeComponentList.size(); + for (int 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() { + int size; + + size = lockGeometryList.size(); + for (int i = 0; i < size; i++) { + GeometryRetained geo = lockGeometryList.get(i); + geo.geomLock.unLock(); + } + + size = dlistLockList.size(); + for (int i = 0; i < size; i++) { + GeometryRetained 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 (int i = 0; i < size; i++) { + ImageComponentRetained nc = (ImageComponentRetained)nodeComponentList.get(i); + nc.geomLock.unLock(); + } + } + + void addGeometryToLockList(GeometryRetained 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(GeometryRetained geo) { + lockGeometryList.remove(geo); + + } + + +void addDirtyReferenceGeometry(GeometryArrayRetained 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(NodeComponentRetained nc) { + dirtyNodeComponentList.add(nc); + } + + + void clearDirtyOrientedRAs() { + int i, nRAs; + RenderAtom ra; + nRAs = dirtyOrientedRAs.size(); + + // clear the dirtyMask + for(i=0; i 0) { + cv = canvases[0]; + } + + if (cv != null) { + 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 remove ga = "+ra.geometryAtom); + ra.setRenderBin(false); + ra.renderMolecule.removeRenderAtom(ra); + if (ra.inDirtyOrientedRAs()) { + dirtyOrientedRAs.remove(ra); + ra.dirtyMask &= ~RenderAtom.IN_DIRTY_ORIENTED_RAs; + } + if (ra.inDepthSortList()) { + dirtyDepthSortRenderAtom.remove(ra); + ra.dirtyMask &= ~RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST; + numDirtyTinfo -= ra.rListInfo.length; + } + + // Assertion check in debug mode + if (VersionInfo.isDebug && dirtyDepthSortRenderAtom.contains(ra)) { + System.err.println("removeARenderAtom: ERROR: RenderAtom not removed from dirty list"); + } + } + + void removeAllRenderAtoms() { + int i; + RenderMolecule rm; + int sz = renderAtoms.size(); + + for (i = 0; i < sz; i++) { + RenderAtom ra = 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(orderedBins.get(i)); + } + orderedBins.clear(); + bgOrderedBins.clear(); + nodeComponentList.clear(); + orientedRAs.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 = ob.orderedCollections.get(i); + if (oc == null) + continue; + + for (k = 0; k < oc.childOrderedBins.size(); k++) { + removeOrderedBin(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.err.println("LightBin = "+obin); + EnvironmentSet envSet = obin.environmentSetList; + while (envSet != null) { + System.err.println(" EnvSet = "+envSet); + AttributeBin abin = envSet.attributeBinList; + while (abin != null) { + System.err.println(" ABin = "+abin); + ShaderBin sbin = abin.shaderBinList; + while (sbin != null) { + System.err.println(" SBin = "+sbin); + TextureBin tbin = sbin.textureBinList; + while (tbin != null) { + System.err.println(" Tbin = "+tbin); + RenderMolecule rm = tbin.opaqueRMList; + System.err.println("===> Begin Dumping OpaqueBin"); + dumpRM(rm); + System.err.println("===> End Dumping OpaqueBin"); + rm = tbin.transparentRMList; + System.err.println("===> Begin Dumping transparentBin"); + dumpRM(rm); + System.err.println("===> End Dumping transparentBin"); + tbin = tbin.next; + } + sbin = sbin.next; + } + abin = abin.next; + } + envSet = envSet.next; + } + obin = obin.next; + } + + } + + void dumpRM(RenderMolecule rm) { + while (rm != null) { + System.err.println(" rm = "+rm+" numRAs = "+rm.numRenderAtoms); + System.err.println(" primaryRenderAtomList = "+ + rm.primaryRenderAtomList); + RenderAtomListInfo rinfo = rm.primaryRenderAtomList; + while (rinfo != null) { + System.err.println(" rinfo = "+rinfo); + System.err.println(" rinfo.ra.localeVwcBounds = " + + rinfo.renderAtom.localeVwcBounds); + System.err.println(" rinfo.ra.ga.so.vwcBounds = " + + rinfo.renderAtom.geometryAtom.source.vwcBounds); + System.err.println(" geometry = "+rinfo.geometry()); + + rinfo = rinfo.next; + } + System.err.println(" separateDlistRenderAtomList = "+ + rm.separateDlistRenderAtomList); + rinfo = rm.separateDlistRenderAtomList; + while (rinfo != null) { + System.err.println(" rinfo = "+rinfo); + System.err.println(" rinfo.ra.localeVwcBounds = " + + rinfo.renderAtom.localeVwcBounds); + System.err.println(" rinfo.ra.ga.so.vwcBounds = " + + rinfo.renderAtom.geometryAtom.source.vwcBounds); + System.err.println(" geometry = "+rinfo.geometry()); + rinfo = rinfo.next; + } + System.err.println(" vertexArrayRenderAtomList = "+ + rm.vertexArrayRenderAtomList); + if (rm.next == null) { + rm= rm.nextMap; + } + else { + rm = rm.next; + } + } + } + + void removeTransparentObject (Object obj) { + // System.err.println("&&&&&&&&&&&&removeTransparentObject r = "+obj); + if (obj instanceof TextureBin) { + TextureBin tb = (TextureBin) obj; + if (tb.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; + tb.parentTInfo = null; + } + else { + int index = allTransparentObjects.indexOf(obj); + if (index == -1) { + // System.err.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; + tb.parentTInfo = null; + } + + } + else { + int index = allTransparentObjects.indexOf(obj); + if (index == -1) { + // System.err.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; + nElements--; + r.parentTInfo[i] = null; + } + } + + } + + void updateTransparentInfo(RenderAtom r) { + // System.err.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.err.println("&&&&&&&&&&&&addTransparentObject r = "+obj); + if (obj instanceof TextureBin) { + TextureBin tb = (TextureBin) obj; + // Background geometry + if (tb.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.err.println("update Centroid 2, ga = "+r.geometryAtom); + r.geometryAtom.updateCentroid(); + if (dirtyDepthSortRenderAtom.add(r)) { + numDirtyTinfo += r.rListInfo.length; + } + /* + else { + System.err.println("addTransparentObject: attempt to add RenderAtom already in dirty list"); + } + */ + r.dirtyMask |= RenderAtom.IN_SORTED_POS_DIRTY_TRANSP_LIST; + // System.err.println("transparentInfo ="+transparentInfo); + } + } + + TransparentRenderingInfo getTransparentInfo() { + return new TransparentRenderingInfo(); + } + + TransparentRenderingInfo computeDirtyAcrossTransparentBins(TextureBin tb, TransparentRenderingInfo startinfo) { + TransparentRenderingInfo tinfo = getTransparentInfo(); + /* + tinfo.lightBin = tb.environmentSet.lightBin; + tinfo.envSet = tb.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); + 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.err.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.err.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; + + 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.err.println("&&&&&&&&mergeDepthSort"); + /* + TransparentRenderingInfo t = oldList; + System.err.println(""); + while (t != null) { + System.err.println("==> old t = "+t); + t = t.next; + } + System.err.println(""); + t = newList; + while (t != null) { + System.err.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 + +// System.err.print("Code path 1 "); +// if (transparencySortComparator!=null) +// if (zval2 > zval1 && (transparencySortComparator.compare(input2, input1)>0)) +// System.err.println("PASS"); +// else +// System.err.println("FAIL"); + + if ((transparencySortComparator==null && zval2 > zval1) || + (transparencySortComparator!=null && (transparencySortComparator.compare(input2, input1)>0))){ + // System.err.println("===> path1"); + if (input1.prev == null) { + input1.prev = input2; + input2.prev = null; + input2.next = oldList; + oldList = input2; + } + else { + // System.err.println("===> path2"); + input2.prev = input1.prev; + input1.prev.next = input2; + input2.next = input1; + input1.prev = input2; + } + input2 = nextN; + } + else { + // System.err.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.err.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; +// if (transparencySortComparator==null) +// while (tinfo != null && r.parentTInfo[i].zVal < tinfo.zVal) { +// prevInfo = tinfo; +// tinfo = tinfo.next; +// } +// else { +// System.err.println("Code Path 2 "); +// if (tinfo!=null && (transparencySortComparator.compare(r.parentTInfo[i], tinfo)<0)==r.parentTInfo[i].zVal < tinfo.zVal) +// System.err.println("PASS"); +// else +// System.err.println("FAIL"); +// while (tinfo != null && transparencySortComparator.compare(r.parentTInfo[i], tinfo)<0) { +// 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 { + if (t == dirtyList) { + // This means that the the item has already been + // added to the dirtyList and is at the head of + // the list; since we ensure no duplicate + // renderAtoms, this should never happen. If it + // does, don't try to add it again. + System.err.println("collectDirtyTRInfo: ERROR: t == dirtyList"); + continue; + } + + // assert(t.prev != null); + 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) { + transparencySortComparator = TransparencySortMap.getComparator(view); + TransparentRenderingInfo tinfo, previnfo, nextinfo; + double curZ; + // System.err.println("&&&&&&&&&&&depthSortAll"); + // Do insertion sort + /* + tinfo = startinfo; + while (tinfo != null) { + System.err.println("Soreted tinfo= "+tinfo+" tinfo.prev = "+tinfo.prev+" tinfo.next = "+tinfo.next); + tinfo = tinfo.next; + } + */ + tinfo = startinfo.next; + while (tinfo != null) { + // System.err.println("====> Doing tinfo = "+tinfo); + nextinfo = tinfo.next; + curZ = tinfo.zVal; + previnfo = tinfo.prev; + // Find the correct location for tinfo + + if (transparencySortComparator==null) { + while (previnfo != null && previnfo.zVal < curZ) { + previnfo = previnfo.prev; + } + } else { +// System.err.println("Code Path 3 "); +// if (tinfo!=null && (transparencySortComparator.compare(previnfo, tinfo)<0)==previnfo.zVal < curZ) +// System.err.println("PASS"); +// else +// System.err.println("FAIL"); + while (previnfo != null && transparencySortComparator.compare(previnfo,tinfo)<0) { + 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.err.println("path2, tinfo.prev = "+tinfo.prev); + // System.err.println("path2, tinfo.next = "+tinfo.next); + } + + } + /* + TransparentRenderingInfo tmp = startinfo; + while (tmp != null) { + System.err.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.err.println("z = "+((double[])distMap.get(tinfo.rInfo.renderAtom))[tinfo.rInfo.index]+" ga = "+tinfo.rInfo.renderAtom.geometryAtom); + tinfo = tinfo.next; + } + System.err.println("\n\n"); + tinfo = startinfo; + while (tinfo != null) { + if (tinfo.render) { + System.err.println("same z = "+((double[])distMap.get(tinfo.rInfo.renderAtom))[tinfo.rInfo.index]+" ga = "+tinfo.rInfo.renderAtom.geometryAtom); + GeometryAtom ga = tinfo.rInfo.renderAtom.geometryAtom; + System.err.println("ga.geometryArray.length = "+ga.geometryArray.length); + for (int k = 0; k < ga.geometryArray.length; k++) { + System.err.println("geometry "+k+" = "+ga.geometryArray[k]); + if (ga.geometryArray[k] != null) { + System.err.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((LightRetained)obj); + } + else if (obj instanceof FogRetained) { + envDirty |= REEVALUATE_FOG; + if (!changedFogs.contains(obj)) + changedFogs.add((FogRetained)obj); + } + else if (obj instanceof AlternateAppearanceRetained) { + altAppearanceDirty = true; + + } + else if (obj instanceof ModelClipRetained) { + envDirty |= REEVALUATE_MCLIP; + if (!changedModelClips.contains(obj)) + changedModelClips.add((ModelClipRetained)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) { + ArrayList viewScopedNodes = (ArrayList)m.args[3]; + ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; + int i; + Object[] nodes = (Object[])m.args[0]; + for (Object n : nodes) { + if (n instanceof LightRetained) { + envDirty |= REEVALUATE_LIGHTS; + if (!changedLts.contains(n)) + changedLts.add((LightRetained)n); + } + else if (n instanceof FogRetained) { + envDirty |= REEVALUATE_FOG; + if (!changedFogs.contains(n)) + changedFogs.add((FogRetained)n); + } + else if (n instanceof BackgroundRetained) { + // If a new background is inserted, then + // re_evaluate to determine if this background + // 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((ModelClipRetained)n); + } + else if (n instanceof GeometryAtom) { + visGAIsDirty = true; + visQuery = true; + } + else if (n instanceof AlternateAppearanceRetained) { + altAppearanceDirty = true; + } + } + + // Handle ViewScoped Nodes + if (viewScopedNodes != null) { + int size = viewScopedNodes.size(); + for (i = 0; i < size; i++) { + NodeRetained n = viewScopedNodes.get(i); + ArrayList vl = 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((LightRetained)n); + } + else if (n instanceof FogRetained) { + envDirty |= REEVALUATE_FOG; + if (!changedFogs.contains(n)) + changedFogs.add((FogRetained)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((ModelClipRetained)n); + } + else if (n instanceof AlternateAppearanceRetained) { + altAppearanceDirty = true; + } + } + // Note: geometryAtom is not part of viewScopedNodes + // Its a part of orginal nodes even if scoped + } + } +} + +@Override +void removeNodes(J3dMessage m) { + ArrayList viewScopedNodes = (ArrayList)m.args[3]; + ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; + int i; + Object[] 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); + } + + // This code segment is to handle the texture resource cleanup + // for Raster object. + GeometryAtom geomAtom = (GeometryAtom) nodes[n]; + if (geomAtom.geometryArray != null) { + for (int ii = 0; ii < geomAtom.geometryArray.length; ii++) { + GeometryRetained geomRetained = geomAtom.geometryArray[ii]; + if ((geomRetained != null) + && (geomRetained instanceof RasterRetained)) { + addTextureResourceFreeList(((RasterRetained) geomRetained).texture); + } + } + } + } + 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(); + for (i = 0; i < size; i++) { + NodeRetained node = viewScopedNodes.get(i); + ArrayList vl = 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 + } + + } + } +} + + @Override + void cleanup() { + releaseAllDisplayListID(); + removeAllRenderAtoms(); + } + + + void freeAllDisplayListResources(Canvas3D cv, Context ctx) { + + assert ctx != null; + + 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, ctx); + } + } + + size = sharedDList.size(); + if (size > 0) { + RenderAtomListInfo arr[] = new RenderAtomListInfo[size]; + arr = sharedDList.toArray(arr); + + GeometryArrayRetained geo; + int mask = (cv.useSharedCtx ? rdr.rendererBit : cv.canvasBit); + + for (i = 0; i < size; i++) { + geo = (GeometryArrayRetained)arr[i].geometry(); + // Fix for Issue 5: free all native display lists and clear the + // context creation bits for this canvas, but don't do anything + // with the geo's user list. + if (geo.dlistId > 0) { + // XXXX: for the shared ctx case, we really should + // only free the display lists if this is the last + // Canvas in the renderer. However, since the + // display lists will be recreated, it doesn't + // really matter. + Canvas3D.freeDisplayList(ctx, geo.dlistId); + geo.resourceCreationMask &= ~mask; + } + } + } + } + + + // 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[] = new RenderAtomListInfo[size]; + arr = sharedDList.toArray(arr); + 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.environmentSet; + ra.renderMolecule.removeRenderAtom(ra); + reInsertAttributeBin(e, ra); + } + } + else { + + // XXXX: handle texture + } + + + } + */ + +} diff --git a/src/javax/media/j3d/RenderMethod.java b/src/javax/media/j3d/RenderMethod.java new file mode 100644 index 0000000..2e603c0 --- /dev/null +++ b/src/javax/media/j3d/RenderMethod.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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, + RenderAtomListInfo ra, int dirtyBits); +} diff --git a/src/javax/media/j3d/RenderMolecule.java b/src/javax/media/j3d/RenderMolecule.java new file mode 100644 index 0000000..269c1a0 --- /dev/null +++ b/src/javax/media/j3d/RenderMolecule.java @@ -0,0 +1,3039 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Vector3d; + +/** + * 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); + + // XXXX: 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; + + // 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++; + } + + // Issue 249 - check for sole user only if property is set + soleUser = false; + if (VirtualUniverse.mc.allowSoleUser) { + if (ga.source.appearance != null) { + soleUser = ((ga.source.appearance.changedFrequent & RM_COMPONENTS) != 0); + } + } + + // Set the appearance only for soleUser case + if (soleUser) + appHandle = ga.source.appearance; + else + appHandle = 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] = new Transform3D(); + localeLocalToVworld[1] = new Transform3D(); + 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.err.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; + + 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++; + } + + // XXXX: 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); + } + /* + // XXXX : 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.err.println("RA's NOT a OrientedShape3DRetained and RM is a ORIENTEDSHAPE3D_MOLECULE "); + + return (false); + } + + // XXXX: 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); + } + + } else if (geo instanceof CompressedGeometryRetained) { + if (this.vertexFormat != + ((CompressedGeometryRetained)geo).getVertexFormat()) { + return (false); + } + } else { + //XXXX: 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 == 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() { + 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 = 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 the molecule type is Raster, then add it to the lock list + 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(r); + } + + if ((textureBin.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 (locale != renderBin.locale) { + 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; + } + + @Override + public void updateObject() { + int i; + RenderAtom renderAtom; + RenderAtomListInfo r; + if (textureBin == null) { + return; + } + + if (addRAs != null) { + while (addRAs != null) { + + numRenderAtoms++; + renderAtom = addRAs; + renderAtom.renderMolecule = this; + renderAtom.added = null; + for (int j = 0; j < renderAtom.rListInfo.length; j++) { + r = 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 + // Issue 113 - ignore multiScreen + if ((( geo.vertexFormat & GeometryArray.BY_REFERENCE)!=0) && + (geo.c4fAllocated == 0) && + ((geo.vertexFormat & GeometryArray.COLOR) != 0) && + useAlpha) { + 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.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) { + if (ga.source.sourceNode instanceof MorphRetained) { + return false; + } + + return geo.canBeInDisplayList(ga.alphaEditable); + } + + // 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.err.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; + + renderAtom.envSet = textureBin.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) { + + 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) { + boolean alphaBlend, alphaTest, textureBlend = false; + + alphaBlend = TransparencyAttributesRetained.useAlpha(definingTransparency); + + 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 + // reference and there is color and we need to use alpha + // Issue 113 - ignore multiScreen + if ((( geo.vertexFormat & GeometryArray.BY_REFERENCE)!=0) && + (geo.c4fAllocated == 0) && + ((geo.vertexFormat & GeometryArray.COLOR) != 0) && + useAlpha) { + 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) { + assert pass < 0; + + 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.maxTexCoordSets)) { + modeSupportDL = false; + } + + /* + System.err.println("texCoord " + texCoordSetMapLen + " " + + cv.maxTexCoordSets + " " + modeSupportDL); + + System.err.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.render(this, cv, primaryRenderAtomList,dirtyBits)) + isVisible = true; + } + else { + if (renderBin.dlistRenderMethod.renderSeparateDlistPerRinfo(this, cv, primaryRenderAtomList,dirtyBits)) + isVisible = true; + + } + } else { + if(cachedVertexArrayRenderMethod.render(this, cv, + primaryRenderAtomList, + dirtyBits)) { + isVisible = true; + } + } + } + } + else { // TEXT3D or ORIENTEDSHAPE3D + + if (primaryRenderAtomList != null) { + if(primaryRenderMethod.render(this, cv, primaryRenderAtomList, + dirtyBits)) { + isVisible = true; + } + } + } + + if (separateDlistRenderAtomList != null) { + if (modeSupportDL) { + if(renderBin.dlistRenderMethod.renderSeparateDlists(this, cv, + separateDlistRenderAtomList, + dirtyBits)) { + isVisible = true; + } + + } else { + if(cachedVertexArrayRenderMethod.render(this, cv, + separateDlistRenderAtomList, + dirtyBits)) { + isVisible = true; + } + } + + } + + // XXXX: In the case of independent primitives such as quads, + // it would still be better to call multi draw arrays + if (vertexArrayRenderAtomList != null) { + if(cachedVertexArrayRenderMethod.render(this, cv, + 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.err.println("updateAttributes"); + int bitMask = geometryType | Canvas3D.MATERIAL_DIRTY| + Canvas3D.COLORINGATTRS_DIRTY| + Canvas3D.TRANSPARENCYATTRS_DIRTY; + + // 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) { + /* System.err.println("updateAttributes setModelViewMatrix (1)"); */ + + Transform3D modelMatrix = + trans[localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD]]; + + if (cv.modelMatrix != modelMatrix) { + /* System.err.println("updateAttributes setModelViewMatrix (2)"); */ + + cv.setModelViewMatrix(cv.ctx, cv.vworldToEc.mat, + modelMatrix); + } + } + + cv.canvasDirty &= ~bitMask; + cv.renderMolecule = this; + } + + void transparentSortRender(Canvas3D cv, int pass, TransparentRenderingInfo tinfo) { + assert pass < 0; + + 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.maxTexCoordSets)) { + modeSupportDL = false; + } + + // System.err.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 + // XXXX: 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.err.println("cachedVertexArrayRenderMethod = "+cachedVertexArrayRenderMethod); + // System.err.println("tinfo.rInfo = "+tinfo.rInfo); + if (modeSupportDL) { + renderBin.dlistRenderMethod.renderSeparateDlistPerRinfo(this, cv, + tinfo.rInfo, + ALL_DIRTY_BITS); + } + else { + cachedVertexArrayRenderMethod.render(this, cv, 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.err.println("cachedVertexArrayRenderMethod = "+cachedVertexArrayRenderMethod); + // System.err.println("tinfo.rInfo = "+tinfo.rInfo); + cachedVertexArrayRenderMethod.render(this, cv, 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, + tinfo.rInfo, + ALL_DIRTY_BITS); + } + else { + cachedVertexArrayRenderMethod.render(this, cv, tinfo.rInfo, + ALL_DIRTY_BITS); + } + tinfo.rInfo.next = save; + } + else { + RenderAtomListInfo save= tinfo.rInfo.next; + primaryRenderMethod.render(this, cv, 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, Context ctx) { + 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) { + Canvas3D.freeDisplayList(ctx, id); + } + ra = ra.next; + } + } + else if (primaryMoleculeType == DLIST_MOLECULE) { + if (displayListId > 0) { + Canvas3D.freeDisplayList(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) { + dirtyAttrsAcrossRms = ALL_DIRTY_BITS; + + 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); + // XXXX : 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.err.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.err.println("rm = "+this+" localTovworld = "+localeLocalToVworld[i]+" localeTranslation = "+localeTranslation); + } + + + boolean isOpaque() { + 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 !TransparencyAttributesRetained.useAlpha(definingTransparency); + } + + + boolean updateNodeComponent() { + // System.err.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.err.println("===>Rm = "+this); + + // System.err.println("===> updating node component, cloned = "+cloned+" material.changedFrequent = "+material.changedFrequent); + // System.err.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.err.println("coloringAttributes and soleUser"); + // System.err.println("coloringAttributes ="+coloringAttributes); + if (coloringAttributes == null) { + definingColoringAttributes = null; + red = 1.0f; + green = 1.0f; + blue = 1.0f; + } else { + // System.err.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.err.println("rm = "+this+"red = "+red+" green = "+green+" blue = "+blue); + boolean newVal = isOpaque() || inOrderedGroup; + return (isOpaqueOrInOG != newVal); + + } + + // Issue 129: method to add or remove all rendering atoms in this + // RenderMolecule to or from the transparent info list when we are + // in depth sorted transparency mode and the RenderMolecule + // changes from opaque to transparent or vice versa. + void addRemoveTransparentObject(RenderBin renderBin, boolean add) { + addRemoveTransparentObject(renderBin, add, primaryRenderAtomList); + addRemoveTransparentObject(renderBin, add, separateDlistRenderAtomList); + addRemoveTransparentObject(renderBin, add, vertexArrayRenderAtomList); + } + + private void addRemoveTransparentObject(RenderBin renderBin, + boolean add, + RenderAtomListInfo rinfo) { + while (rinfo != null) { + if (add) { + renderBin.addTransparentObject(rinfo.renderAtom); + } + else { + renderBin.removeTransparentObject(rinfo.renderAtom); + } + rinfo = rinfo.next; + } + } + + 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; + } + + @Override + VirtualUniverse getVirtualUniverse() { + return null; + } + + + void handleLocaleChange() { + if (locale == renderBin.locale) { + if (localToVworld != localeLocalToVworld) { + 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] = 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. + */ + @Override + 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/javax/media/j3d/Renderer.java b/src/javax/media/j3d/Renderer.java new file mode 100644 index 0000000..6362672 --- /dev/null +++ b/src/javax/media/j3d/Renderer.java @@ -0,0 +1,1751 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * 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.awt.GraphicsConfiguration; +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.logging.Level; + + +class Renderer extends J3dThread { + + // 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(); + + 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 + Context sharedCtx = null; + + // 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 and drawable, used to free shared context + private Drawable sharedCtxDrawable = null; + + /** + * This is the id of the current rendering context + */ + Context currentCtx = null; + + /** + * This is the id of the current rendering drawable + */ + Drawable currentDrawable = null; + + // an unique bit to identify this renderer + int rendererBit = 0; + // an unique number to identify this renderer : ( rendererBit = 1 << rendererId) + int rendererId = 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(); + + 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 + Transform3D accumLeftProj = new Transform3D(); + Transform3D accumRightProj = new Transform3D(); + Transform3D accumInfLeftProj = new Transform3D(); + Transform3D accumInfRightProj = new Transform3D(); + + // 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; + + // 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); + + // Instrumentation of Java 3D renderer + private long lastSwapTime = System.nanoTime(); + + private synchronized int newInstanceNum() { + return (++numInstances); + } + + @Override + 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; + rendererId = VirtualUniverse.mc.getRendererId(); + rendererBit = (1 << rendererId); + renderMessage = new J3dMessage[1]; + } + + + /** + * The main loop for the renderer. + */ + @Override + void doWork(long referenceTime) { + RenderBin renderBin = null; + Canvas3D cv, canvas=null; + Object firstArg; + View view = null; + int stereo_mode; + int num_stereo_passes, num_accum_passes = 1; + int pass, apass, i, j; + 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; + 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 0; + + c.userStencilAvailable = + (userOwnsStencil && (c.actualStencilSize > 0)); + c.systemStencilAvailable = + (!userOwnsStencil && (c.actualStencilSize > 0)); + + c.sceneAntialiasingMultiSamplesAvailable = + c.hasSceneAntialiasingMultisample(); + + if (c.sceneAntialiasingMultiSamplesAvailable) { + c.sceneAntialiasingAvailable = true; + } else { + c.sceneAntialiasingAvailable = + c.hasSceneAntialiasingAccum(); + } + } catch (RuntimeException ex) { + ex.printStackTrace(); + + // Issue 260 : indicate fatal error and notify error listeners + c.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.GRAPHICS_CONFIG_ERROR, + J3dI18N.getString("Renderer1")); + err.setCanvas3D(c); + err.setGraphicsDevice(c.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + } + GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY); + } else if (reqType == MasterControl.SET_QUERYPROPERTIES){ + try { + c.createQueryContext(); + } catch (RuntimeException ex) { + ex.printStackTrace(); + + // Issue 260 : indicate fatal error and notify error listeners + c.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, + J3dI18N.getString("Renderer2")); + err.setCanvas3D(c); + err.setGraphicsDevice(c.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + } + // currentCtx change after we create a new context + GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY); + currentCtx = null; + currentDrawable = null; + } + } else if (secondArg instanceof Integer) { + // Issue 121 - This was formerly used as a message from + // the now-nonexistant TextureRetained finalize() method + // to free the texture id + throw new AssertionError(); + } 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) { + GraphicsConfiguration gcfg = null; + GraphicsConfiguration [] gcList = (GraphicsConfiguration []) gct.testCfg; + try { + gcfg = Pipeline.getPipeline().getBestConfiguration(gct, gcList); + } catch (NullPointerException npe) { + npe.printStackTrace(); + } catch (RuntimeException ex) { + ex.printStackTrace(); + + // Issue 260 : notify error listeners + RenderingError err = + new RenderingError(RenderingError.GRAPHICS_CONFIG_ERROR, + J3dI18N.getString("Renderer3")); + err.setGraphicsDevice(gcList[0].getDevice()); + notifyErrorListeners(err); + } + + gct.testCfg = gcfg; + } else if (reqType == MasterControl.ISCONFIGSUPPORT) { + boolean rval = false; + GraphicsConfiguration gc = (GraphicsConfiguration) gct.testCfg; + try { + if (Pipeline.getPipeline().isGraphicsConfigSupported(gct, gc)) { + rval = true; + } + } catch (NullPointerException npe) { + npe.printStackTrace(); + } catch (RuntimeException ex) { + ex.printStackTrace(); + + // Issue 260 : notify error listeners + RenderingError err = + new RenderingError(RenderingError.GRAPHICS_CONFIG_ERROR, + J3dI18N.getString("Renderer4")); + err.setGraphicsDevice(gc.getDevice()); + notifyErrorListeners(err); + } + + gct.testCfg = Boolean.valueOf(rval); + } + GraphicsConfigTemplate3D.runMonitor(J3dThread.NOTIFY); + } + + m[nmesg++].decRefcount(); + continue; + } + + canvas = (Canvas3D) firstArg; + + renderType = m[nmesg].type; + + if (renderType == J3dMessage.CREATE_OFFSCREENBUFFER) { + // Fix for issue 18. + // Fix for issue 20. + + canvas.drawable = null; + try { + // Issue 396. Pass in a null ctx for 2 reasons : + // 1) We should not use ctx field directly without buffering in a msg. + // 2) canvas.ctx should be null. + canvas.drawable = + canvas.createOffScreenBuffer(null, + canvas.offScreenCanvasSize.width, + canvas.offScreenCanvasSize.height); + } catch (RuntimeException ex) { + ex.printStackTrace(); + } + + if (canvas.drawable == null) { + // Issue 260 : indicate fatal error and notify error listeners + canvas.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.OFF_SCREEN_BUFFER_ERROR, + J3dI18N.getString("Renderer5")); + err.setCanvas3D(canvas); + err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + } + + canvas.offScreenBufferPending = false; + m[nmesg++].decRefcount(); + continue; + } + else if (renderType == J3dMessage.DESTROY_CTX_AND_OFFSCREENBUFFER) { + Object[] obj = m[nmesg].args; + + // Fix for issue 175: destroy ctx & off-screen buffer + // Fix for issue 340: get display, drawable & ctx from msg + removeCtx(canvas, + (Drawable) obj[2], + (Context) obj[3], + false, !canvas.offScreen, true); + + canvas.offScreenBufferPending = false; + m[nmesg++].decRefcount(); + continue; + } else if (renderType == J3dMessage.ALLOCATE_CANVASID) { + canvas.allocateCanvasId(); + } else if (renderType == J3dMessage.FREE_CANVASID) { + canvas.freeCanvasId(); + } + + 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) { + // render the image again after resize + VirtualUniverse.mc.sendRunMessage(canvas.view, J3dThread.RENDER_THREAD); + m[nmesg++].decRefcount(); + } else if (renderType == J3dMessage.TOGGLE_CANVAS) { + 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.err.println("command= " + command); + if (canvas.isFatalError()) { + continue; + } + + try { + + 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); + break; + case GraphicsContext3D.MULTIPLY_MODEL_TRANSFORM: + t3d = (Transform3D)m[nmesg].args[2]; + canvas.graphicsContext3D.doMultiplyModelTransform(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.DISPOSE2D: + // Issue 583 - the graphics2D field may be null here + if (canvas.graphics2D != null) { + canvas.graphics2D.doDispose(); + } + break; + case GraphicsContext3D.SET_MODELCLIP: + canvas.graphicsContext3D.doSetModelClip( + (ModelClip)m[nmesg].args[2]); + break; + default: + break; + } + + } catch (RuntimeException ex) { + ex.printStackTrace(); + + // Issue 260 : indicate fatal error and notify error listeners + canvas.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, + J3dI18N.getString("Renderer6")); + err.setCanvas3D(canvas); + err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + } + + m[nmesg++].decRefcount(); + } else { // retained mode rendering + long startRenderTime = 0L; + if (MasterControl.isStatsLoggable(Level.INFO)) { + // Instrumentation of Java 3D renderer + startRenderTime = System.nanoTime(); + } + + m[nmesg++].decRefcount(); + + if (canvas.isFatalError()) { + continue; + } + + ImageComponent2DRetained offBufRetained = null; + + if (renderType == J3dMessage.RENDER_OFFSCREEN) { + // Issue 131: set offScreenRendering flag here, since it + // otherwise won't be set for auto-off-screen rendering + // (which doesn't use renderOffScreenBuffer) + canvas.offScreenRendering = true; + if (canvas.drawable == null || !canvas.active) { + canvas.offScreenRendering = false; + continue; + } else { + offBufRetained = (ImageComponent2DRetained) + canvas.offScreenBuffer.retained; + + if (offBufRetained.isByReference()) { + offBufRetained.geomLock.getLock(); + } + + offBufRetained.evaluateExtensions(canvas); + + } + + } else if (!canvas.active) { + continue; + } + + // Issue 78 - need to get the drawingSurface info every + // frame; this is necessary since the HDC (window ID) + // on Windows can become invalidated without our + // being notified! + if (!canvas.offScreen) { + canvas.drawingSurfaceObject.getDrawingSurfaceObjectInfo(); + } + + 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 == null) { + sharedCtxDrawable = canvas.drawable; + + // Always lock for context create + if (!canvas.drawingSurfaceObject.renderLock()) { + if ((offBufRetained != null) && + offBufRetained.isByReference()) { + offBufRetained.geomLock.unLock(); + } + canvas.offScreenRendering = false; + break doneRender; + } + + synchronized (VirtualUniverse.mc.contextCreationLock) { + sharedCtx = null; + try { + sharedCtx = canvas.createNewContext(null, true); + } catch (RuntimeException ex) { + ex.printStackTrace(); + } + + if (sharedCtx == null) { + canvas.drawingSurfaceObject.unLock(); + if ((offBufRetained != null) && + offBufRetained.isByReference()) { + offBufRetained.geomLock.unLock(); + } + canvas.offScreenRendering = false; + + // Issue 260 : indicate fatal error and notify error listeners + canvas.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, + J3dI18N.getString("Renderer7")); + err.setCanvas3D(canvas); + err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + + break doneRender; + } + sharedCtxTimeStamp = + VirtualUniverse.mc.getContextTimeStamp(); + + needToRebuildDisplayList = true; + } + + canvas.drawingSurfaceObject.unLock(); + } + } + + if (canvas.ctx == null) { + + // Always lock for context create + if (!canvas.drawingSurfaceObject.renderLock()) { + if ((offBufRetained != null) && + offBufRetained.isByReference()) { + offBufRetained.geomLock.unLock(); + } + canvas.offScreenRendering = false; + break doneRender; + } + + synchronized (VirtualUniverse.mc.contextCreationLock) { + canvas.ctx = null; + try { + canvas.ctx = canvas.createNewContext(sharedCtx, false); + } catch (RuntimeException ex) { + ex.printStackTrace(); + } + + if (canvas.ctx == null) { + canvas.drawingSurfaceObject.unLock(); + if ((offBufRetained != null) && + offBufRetained.isByReference()) { + offBufRetained.geomLock.unLock(); + } + canvas.offScreenRendering = false; + + // Issue 260 : indicate fatal error and notify error listeners + canvas.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.CONTEXT_CREATION_ERROR, + J3dI18N.getString("Renderer7")); + err.setCanvas3D(canvas); + err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + + break doneRender; + } + + if (canvas.graphics2D != null) { + canvas.graphics2D.init(); + } + + canvas.ctxTimeStamp = + VirtualUniverse.mc.getContextTimeStamp(); + listOfCtxs.add(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); + if(nc instanceof ImageComponentRetained) { + ((ImageComponentRetained)nc).evaluateExtensions(canvas); + } + } + } + + // enable separate specular color + canvas.enableSeparateSpecularColor(); + } + + // create the cache texture state in canvas + // for state download checking purpose + if (canvas.texUnitState == null) { + canvas.createTexUnitState(); + } + + canvas.resetImmediateRendering(); + 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(); + } + canvas.offScreenRendering = false; + break doneRender; + } + + // handle free resource + if (canvas.useSharedCtx) { + freeResourcesInFreeList(canvas); + } else { + canvas.freeResourcesInFreeList(canvas.ctx); + } + + if (VirtualUniverse.mc.doDsiRenderLock) { + canvas.drawingSurfaceObject.unLock(); + } + + // Issue 109 : removed copyOfCvCache now that we have + // a separate canvasViewCache for computing view frustum + CanvasViewCache cvCache = canvas.canvasViewCache; + + // Deadlock if we include updateViewCache in + // drawingSurfaceObject sync. + canvas.updateViewCache(false, null, null, + renderBin.geometryBackground != null); + + if ((VirtualUniverse.mc.doDsiRenderLock) && + (!canvas.drawingSurfaceObject.renderLock())) { + if ((offBufRetained != null) && + offBufRetained.isByReference()) { + offBufRetained.geomLock.unLock(); + } + canvas.offScreenRendering = false; + break doneRender; + } + + int cvWidth = cvCache.getCanvasWidth(); + int cvHeight = cvCache.getCanvasHeight(); + // setup viewport + canvas.setViewport(canvas.ctx, 0, 0, cvWidth, cvHeight); + + // 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--) { + 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 = cvCache.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 (((canvas.extensionsSupported & Canvas3D.MULTISAMPLE) == 0) || + !canvas.sceneAntialiasingMultiSamplesAvailable) { + doAccum = true; + num_accum_passes = NUM_ACCUMULATION_SAMPLES; + + System.arraycopy( + cvCache.getLeftProjection().mat, + 0, accumLeftProj.mat, 0, 16); + + + accumDxFactor = ( + canvas.canvasViewCache.getPhysicalWindowWidth() / + canvas.canvasViewCache.getCanvasWidth())*canvas.view.fieldOfView; + + accumDyFactor = ( + canvas.canvasViewCache.getPhysicalWindowHeight() / + canvas.canvasViewCache.getCanvasHeight())*canvas.view.fieldOfView; + + + accumLeftX = accumLeftProj.mat[3]; + accumLeftY = accumLeftProj.mat[7]; + + if (useStereo) { + System.arraycopy( + cvCache.getRightProjection().mat, + 0, accumRightProj.mat, 0, 16); + accumRightX = accumRightProj.mat[3]; + accumRightY = accumRightProj.mat[7]; + } + + if (renderBin.geometryBackground != null) { + System.arraycopy( + cvCache.getInfLeftProjection().mat, + 0, accumInfLeftProj.mat, 0, 16); + accumInfLeftX = accumInfLeftProj.mat[3]; + accumInfLeftY = accumInfLeftProj.mat[7]; + if (useStereo) { + System.arraycopy( + cvCache.getInfRightProjection().mat, + 0, accumInfRightProj.mat, 0, 16); + accumInfRightX = accumInfRightProj.mat[3]; + accumInfRightY = accumInfRightProj.mat[7]; + } + } + } else { + + if (!canvas.isAntialiasingSet()) { + // System.err.println("Renderer : Enable FullSceneAntialiasing"); + + canvas.setFullSceneAntialiasing(canvas.ctx, true); + } + } + } else { + + if (canvas.isAntialiasingSet()) { + // System.err.println("Renderer : Disable SceneAntialiasing"); + canvas.setFullSceneAntialiasing(canvas.ctx, 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); + + // clear background if not full screen antialiasing + // and not in stereo mode + if (!doAccum && !sharedStereoZBuffer) { + BackgroundRetained bg = renderBin.background; + + canvas.clear(bg, cvWidth, cvHeight); + + } + + // 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(); + } catch (Error e) { + // Issue 264 - catch Error so Renderer doesn't die + System.err.println("Error 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(); + } + canvas.offScreenRendering = false; + 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; + + accumLeftProj.mat[3] = accumLeftX + + accumLeftProj.mat[0] * accumDx + + accumLeftProj.mat[1] * accumDy; + + accumLeftProj.mat[7] = accumLeftY + + accumLeftProj.mat[4] * accumDx + + accumLeftProj.mat[5] * accumDy; + + if (useStereo) { + accumRightProj.mat[3] = accumRightX + + accumRightProj.mat[0] * accumDx + + accumRightProj.mat[1] * accumDy; + + accumRightProj.mat[7] = accumRightY + + accumRightProj.mat[4] * accumDx + + accumRightProj.mat[5] * accumDy; + } + + if (renderBin.geometryBackground != null) { + accumInfLeftProj.mat[3] = accumInfLeftX + + accumInfLeftProj.mat[0] * accumDx + + accumInfLeftProj.mat[1] * accumDy; + + accumInfLeftProj.mat[7] = accumInfLeftY + + accumInfLeftProj.mat[4] * accumDx + + accumInfLeftProj.mat[5] * accumDy; + + if (useStereo) { + accumInfRightProj.mat[3] = + accumInfRightX + + accumInfRightProj.mat[0] * accumDx + + accumInfRightProj.mat[1] * accumDy; + + accumInfRightProj.mat[7] = + accumInfRightY + + accumInfRightProj.mat[4] * accumDx + + accumInfRightProj.mat[5] * accumDy; + } + } + } + + // clear background for stereo and + // accumulation buffer cases + if (doAccum || sharedStereoZBuffer) { + BackgroundRetained bg = renderBin.background; + + canvas.clear(bg, cvWidth, cvHeight); + + } + + // render background geometry + if (renderBin.geometryBackground != null) { + + // setup rendering matrices + if (pass == 0) { + canvas.vpcToEc = + cvCache.getInfLeftVpcToEc(); + if (doAccum) { + canvas.setProjectionMatrix( + canvas.ctx, accumInfLeftProj); + } else { + canvas.setProjectionMatrix( + canvas.ctx, + cvCache.getInfLeftProjection()); + } + } else { + canvas.vpcToEc = + cvCache.getInfRightVpcToEc(); + if (doAccum) { + canvas.setProjectionMatrix( + canvas.ctx, accumInfRightProj); + } else { + canvas.setProjectionMatrix( + canvas.ctx, + cvCache.getInfRightProjection()); + } + } + canvas.vworldToEc.mul(canvas.vpcToEc, + cvCache.getInfVworldToVpc()); + + // render background geometry + renderBin.renderBackground(canvas); + } + + // setup rendering matrices + if (pass == 0) { + canvas.vpcToEc = cvCache.getLeftVpcToEc(); + if (doAccum) { + canvas.setProjectionMatrix(canvas.ctx, accumLeftProj); + } else { + canvas.setProjectionMatrix(canvas.ctx, + cvCache.getLeftProjection()); + } + } else { + canvas.vpcToEc = cvCache.getRightVpcToEc(); + if (doAccum) { + canvas.setProjectionMatrix( + canvas.ctx, accumRightProj); + } else { + canvas.setProjectionMatrix(canvas.ctx, + cvCache.getRightProjection()); + } + } + canvas.vworldToEc.mul(canvas.vpcToEc, + cvCache.getVworldToVpc()); + + + synchronized (cvCache) { + if (pass == 0) { + canvas.setFrustumPlanes(cvCache.getLeftFrustumPlanesInVworld()); + } else { + canvas.setFrustumPlanes(cvCache.getRightFrustumPlanesInVworld()); + } + } + + // Force view matrix dirty for each eye. + if (useStereo) { + canvas.canvasDirty |= Canvas3D.VIEW_MATRIX_DIRTY; + } + + // 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(); + } catch (Error e) { + // Issue 264 - catch Error so Renderer doesn't die + System.err.println("Error 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(); + } + canvas.offScreenRendering = false; + 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(); + } catch (Error e) { + // Issue 264 - catch Error so Renderer doesn't die + System.err.println("Error occurred during Canvas3D callback:"); + e.printStackTrace(); + } + canvas.view.inCanvasCallback = false; + + // end offscreen rendering + if (canvas.offScreenRendering) { + + canvas.syncRender(canvas.ctx, true); + canvas.endOffScreenRendering(); + canvas.offScreenRendering = false; + + // Issue 489 - don't call postSwap here for auto-offscreen, + // since it will be called later by the SWAP operation + if (canvas.manualRendering) { + // 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(); + } catch (Error e) { + // Issue 264 - catch Error so Renderer doesn't die + System.err.println("Error occurred during Canvas3D callback:"); + e.printStackTrace(); + } + + if (offBufRetained.isByReference()) { + offBufRetained.geomLock.unLock(); + } + + canvas.view.inCanvasCallback = false; + + canvas.releaseCtx(); + } + } + + if (MasterControl.isStatsLoggable(Level.INFO)) { + // Instrumentation of Java 3D renderer + long deltaTime = System.nanoTime() - startRenderTime; + VirtualUniverse.mc.recordTime(MasterControl.TimeType.RENDER, deltaTime); + } + + } 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) { + // Print NPE, but otherwise ignore it + ne.printStackTrace(); + if (canvas != null) { + // drawingSurfaceObject will safely ignore + // this request if this is not lock before + canvas.drawingSurfaceObject.unLock(); + + } + } catch (RuntimeException ex) { + ex.printStackTrace(); + + if (canvas != null) { + // drawingSurfaceObject will safely ignore + // this request if this is not lock before + canvas.drawingSurfaceObject.unLock(); + } + + // Issue 260 : indicate fatal error and notify error listeners + canvas.setFatalError(); + RenderingError err = + new RenderingError(RenderingError.UNEXPECTED_RENDERING_ERROR, + J3dI18N.getString("Renderer8")); + err.setCanvas3D(canvas); + err.setGraphicsDevice(canvas.graphicsConfiguration.getDevice()); + notifyErrorListeners(err); + } + } + +// resource clean up +@Override +void shutdown() { + removeAllCtxs(); +} + + @Override + void cleanup() { + super.cleanup(); + renderMessage = new J3dMessage[1]; + rendererStructure = new RendererStructure(); + bgVworldToVpc = new Transform3D(); + sharedCtx = null; + sharedCtxTimeStamp = 0; + sharedCtxDrawable = null; + dirtyRenderMoleculeList.clear(); + dirtyRenderAtomList.clear(); + dirtyDlistPerRinfoList.clear(); + textureIdResourceFreeList.clear(); + displayListResourceFreeList.clear(); + onScreen = null; + offScreen = null; + m = null; + nmesg = 0; + } + + + // This is only invoked from removeCtx()/removeAllCtxs() + // with drawingSurface already lock + final void makeCtxCurrent(Context sharedCtx, Drawable drawable) { + if (sharedCtx != currentCtx || drawable != currentDrawable) { + Canvas3D.useCtx(sharedCtx, drawable); + /* + if(!Canvas3D.useCtx(sharedCtx, display, drawable)) { + Thread.dumpStack(); + System.err.println("useCtx Fail"); + } + */ + currentCtx = sharedCtx; + currentDrawable = drawable; + } + } + + // 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. + private void removeCtx(Canvas3D cv, Drawable drawable, Context ctx, + boolean resetCtx, boolean freeBackground, + boolean destroyOffScreenBuffer) { + + synchronized (VirtualUniverse.mc.contextCreationLock) { + if (ctx != null) { + int idx = listOfCtxs.indexOf(ctx); + if (idx >= 0) { + listOfCtxs.remove(idx); + listOfCanvases.remove(idx); + // Issue 326 : don't check display variable here + if ((drawable != null) && 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 != null) { + if (listOfCtxs.isEmpty()) { + makeCtxCurrent(sharedCtx, sharedCtxDrawable); + freeResourcesInFreeList(null); + freeContextResources(); + Canvas3D.destroyContext(sharedCtxDrawable, sharedCtx); + currentCtx = null; + currentDrawable = null; + } else { + freeResourcesInFreeList(cv); + } + cv.makeCtxCurrent(ctx, drawable); + } else { + cv.makeCtxCurrent(ctx, drawable); + cv.freeResourcesInFreeList(ctx); + } + cv.freeContextResources(this, freeBackground, ctx); + Canvas3D.destroyContext(drawable, ctx); + currentCtx = null; + currentDrawable = null; + cv.drawingSurfaceObject.unLock(); + } + } + } + + if (resetCtx) { + cv.ctx = null; + } + + if ((sharedCtx != null) && listOfCtxs.isEmpty()) { + sharedCtx = null; + sharedCtxTimeStamp = 0; + } + cv.ctxTimeStamp = 0; + } + + // Fix for issue 18. + // Since we are now the renderer thread, + // we can safely execute destroyOffScreenBuffer. + if(destroyOffScreenBuffer) { + cv.destroyOffScreenBuffer(ctx, drawable); + cv.offScreenBufferPending = false; + } + } + } + + void removeAllCtxs() { + Canvas3D cv; + + synchronized (VirtualUniverse.mc.contextCreationLock) { + + for (int i=listOfCanvases.size()-1; i >=0; i--) { + cv = listOfCanvases.get(i); + + if ((cv.screen != null) && (cv.ctx != null)) { + // Issue 326 : don't check display variable here + if ((cv.drawable != null) && 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 != null)) { + makeCtxCurrent(sharedCtx, sharedCtxDrawable); + freeResourcesInFreeList(null); + freeContextResources(); + Canvas3D.destroyContext(sharedCtxDrawable, sharedCtx); + currentCtx = null; + currentDrawable = null; + } + cv.makeCtxCurrent(); + cv.freeResourcesInFreeList(cv.ctx); + cv.freeContextResources(this, true, cv.ctx); + Canvas3D.destroyContext(cv.drawable, cv.ctx); + currentCtx = null; + currentDrawable = null; + cv.drawingSurfaceObject.unLock(); + } + } + } + + cv.ctx = null; + cv.ctxTimeStamp = 0; + } + + if (sharedCtx != null) { + sharedCtx = null; + sharedCtxTimeStamp = 0; + } + listOfCanvases.clear(); + listOfCtxs.clear(); + } + } + + // handle free resource in the FreeList + void freeResourcesInFreeList(Canvas3D cv) { + Iterator it; + boolean isFreeTex = (textureIdResourceFreeList.size() > 0); + boolean isFreeDL = (displayListResourceFreeList.size() > 0); + int val; + + if (isFreeTex || isFreeDL) { + if (cv != null) { + cv.makeCtxCurrent(sharedCtx); + } + + if (isFreeDL) { + for (it = displayListResourceFreeList.iterator(); it.hasNext();) { + val = it.next().intValue(); + if (val <= 0) { + continue; + } + Canvas3D.freeDisplayList(sharedCtx, val); + } + displayListResourceFreeList.clear(); + } + if (isFreeTex) { + for (it = textureIdResourceFreeList.iterator(); it.hasNext();) { + val = it.next().intValue(); + if (val <= 0) { + continue; + } + if (val >= textureIDResourceTable.size()) { + MasterControl.getCoreLogger().severe( + "Error in freeResourcesInFreeList : ResourceIDTableSize = " + + textureIDResourceTable.size() + + " val = " + val); + } else { + TextureRetained tex = textureIDResourceTable.get(val); + if (tex != null) { + synchronized (tex.resourceLock) { + tex.resourceCreationMask &= ~rendererBit; + if (tex.resourceCreationMask == 0) { + tex.freeTextureId(val); + } + } + } + + textureIDResourceTable.set(val, null); + } + Canvas3D.freeTexture(sharedCtx, val); + } + textureIdResourceFreeList.clear(); + } + if (cv != null) { + cv.makeCtxCurrent(cv.ctx); + } + } + } + +final void addTextureResource(int id, TextureRetained 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() { + TextureRetained tex; + + for (int id = textureIDResourceTable.size()-1; id >= 0; id--) { + tex = textureIDResourceTable.get(id); + if (tex == null) { + continue; + } + Canvas3D.freeTexture(sharedCtx, id); + synchronized (tex.resourceLock) { + tex.resourceCreationMask &= ~rendererBit; + if (tex.resourceCreationMask == 0) { + tex.freeTextureId(id); + } + } + } + textureIDResourceTable.clear(); + + // displayList is free in Canvas.freeContextResources() + } + + /** + * Send a message to the notification thread, which will call the + * shader error listeners. + */ + static void notifyErrorListeners(RenderingError err) { + J3dNotification notification = new J3dNotification(); + notification.type = J3dNotification.RENDERING_ERROR; + notification.universe = null;//cv.view.universe; + notification.args[0] = err; + VirtualUniverse.mc.sendNotification(notification); + } + + + // Default rendering error listener class + private static RenderingErrorListener defaultErrorListener = null; + + synchronized static RenderingErrorListener getDefaultErrorListener() { + if (defaultErrorListener == null) { + defaultErrorListener = new DefaultErrorListener(); + } + + return defaultErrorListener; + } + + static class DefaultErrorListener implements RenderingErrorListener { + @Override + public void errorOccurred(RenderingError error) { + System.err.println(); + System.err.println("DefaultRenderingErrorListener.errorOccurred:"); + error.printVerbose(); + System.exit(1); + } + } + +} diff --git a/src/javax/media/j3d/RendererStructure.java b/src/javax/media/j3d/RendererStructure.java new file mode 100644 index 0000000..b8dbc6c --- /dev/null +++ b/src/javax/media/j3d/RendererStructure.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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; + } + + + @Override + void processMessages(long referenceTime) {} + + @Override + void removeNodes(J3dMessage m) {} + + @Override + void cleanup() {} +} diff --git a/src/javax/media/j3d/RenderingAttributes.java b/src/javax/media/j3d/RenderingAttributes.java new file mode 100644 index 0000000..0b545c7 --- /dev/null +++ b/src/javax/media/j3d/RenderingAttributes.java @@ -0,0 +1,1501 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The RenderingAttributes object defines common rendering attributes + * for all primitive types. The rendering attributes are:

+ *

    + *
  • Depth test function - used to compare the incoming (source) depth of + * each pixel with depth of the pixel stored in frame buffer. If the test + * passes, the pixel is written, otherwise the pixel is not written. The depth test + * function is set with the setDepthTestFunction + * method. By default, LESS_OR_EQUAL is the function used. The depth test + * function is one of the following:
  • + *

      + *
    • ALWAYS - pixels are always drawn, irrespective of the depth + * value. This effectively disables depth testing.
    • + * + *

    • NEVER - pixels are never drawn, irrespective of the depth + * value.
    • + * + *

    • EQUAL - pixels are drawn if the incoming pixel depth is equal + * to the stored pixel depth in the frame buffer.
    • + * + *

    • NOT_EQUAL - pixels are drawn if the incoming pixel depth is + * not equal to the stored pixel depth in the frame buffer.
    • + * + *

    • LESS - pixels are drawn if the incoming pixel depth is less + * than the stored pixel depth in the frame buffer.
    • + * + *

    • LESS_OR_EQUAL - pixels are drawn if the incoming pixel depth + * is less than or equal to the stored pixel depth in the frame buffer. + * This is the default setting.
    • + * + *

    • GREATER - pixels are drawn if the incoming pixel depth is greater + * than the stored pixel depth in the frame buffer.
    • + * + *

    • GREATER_OR_EQUAL - pixels are drawn if the incoming pixel depth + * is greater than or equal to the stored pixel depth in the frame buffer.
    • + *

    + * + *
  • Alpha test function - used to compare the incoming (source) alpha value + * of each pixel with the alpha test value. If the test passes, the pixel is + * written, otherwise the pixel is not written. The alpha test + * function is set with the setAlphaTestFunction + * method. The alpha test + * function is one of the following:
  • + *

      + *
    • ALWAYS - pixels are always drawn, irrespective of the alpha + * value. This effectively disables alpha testing. + * This is the default setting.
    • + * + *

    • NEVER - pixels are never drawn, irrespective of the alpha + * value.
    • + * + *

    • EQUAL - pixels are drawn if the incoming pixel alpha value is equal + * to the alpha test value.
    • + * + *

    • NOT_EQUAL - pixels are drawn if the incoming pixel alpha value is + * not equal to the alpha test value.
    • + * + *

    • LESS - pixels are drawn if the incoming pixel alpha value is less + * than the alpha test value.
    • + * + *

    • LESS_OR_EQUAL - pixels are drawn if the incoming pixel alpha value + * is less than or equal to the alpha test value.
    • + * + *

    • GREATER - pixels are drawn if the incoming pixel alpha value is greater + * than the alpha test value.
    • + * + *

    • GREATER_OR_EQUAL - pixels are drawn if the incoming pixel alpha + * value is greater than or equal to the alpha test value.
    • + *

    + * + *
  • 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 setAlphaTestValue + * method. The default alpha test value is 0.0.
  • + * + *

  • Raster operation - the raster operation function for this + * RenderingAttributes component object. The raster operation is + * set with the setRasterOp method. The raster operation + * is enabled or disabled with the setRasterOpEnable + * method. The raster operation is one of the following:
  • + *

      + *
    • ROP_CLEAR - DST = 0.
    • + *
    • ROP_AND DST = SRC & DST.
    • + *
    • ROP_AND_REVERSE DST = SRC & ~DST.
    • + *
    • ROP_COPY - DST = SRC. This is the default operation.
    • + *
    • ROP_AND_INVERTED - DST = ~SRC & DST.
    • + *
    • ROP_NOOP - DST = DST.
    • + *
    • ROP_XOR - DST = SRC ^ DST.
    • + *
    • ROP_OR - DST = DST | SRC.
    • + *
    • ROP_NOR - DST = ~( DST | SRC .)
    • + *
    • ROP_EQUIV - DST = ~( DST ^ SRC .)
    • + *
    • ROP_INVERT - DST = ~DST.
    • + *
    • ROP_OR_REVERSE - DST = src | ~DST.
    • + *
    • ROP_COPY_INVERTED - DST = ~SRC.
    • + *
    • ROP_OR_INVERTED - DST = ~SRC | DST.
    • + *
    • ROP_NAND - DST = ~(SRC & DST.)
    • + *
    • ROP_SET - DST = 1.
    • + *

    + *
  • Vertex colors - vertex colors can be ignored for this + * RenderingAttributes object. This capability is set with the + * setIgnoreVertexColors 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.
  • + * + *

  • 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 setVisible + * method. By default, the visibility flag is true.
  • + * + *

  • Depth buffer - can be enabled or disabled for this + * RenderingAttributes component object. The + * setDepthBufferEnable method enables + * or disabled the depth buffer. The + * setDepthBufferWriteEnable 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.
  • + * + *

  • Stencil buffer - can be enabled or disabled for this RenderingAttributes + * component object using the setStencilEnable method. If the + * stencil buffer is disabled, the stencil operation and function are ignored. + * If a scene graph is rendered on a Canvas3D that does not have a stencil + * buffer, the stencil buffer will be implicitly disabled for that + * canvas.
  • + * + *

  • Stencil write mask - mask that controls which bits of the stencil + * buffer are written when the stencil buffer is enabled. The default value is + * ~0 (all ones).
  • + * + *

  • Stencil operation - a set of three stencil operations performed + * when: 1) the stencil test fails; 2) the stencil test passes, but + * the depth test fails; or 3) both the stencil test and depth test pass. + * The stencil operations are set with the setStencilOp + * method. The stencil operation is one of the following:
  • + *

      + *
    • STENCIL_KEEP - keeps the current value (no operation performed). + * This is the default setting.
    • + *
    • STENCIL_ZERO - Sets the stencil buffer value to 0.
    • + *
    • STENCIL_REPLACE - Sets the stencil buffer value to + * refValue, as specified by setStencilFunction.
    • + *
    • STENCIL_INCR - Increments the current stencil buffer value.
    • + *
    • STENCIL_DECR - Decrements the current stencil buffer value.
    • + *
    • STENCIL_INVERT - Bitwise inverts the current stencil buffer value.
    • + *

    + * + *
  • Stencil test function - used to compare the stencil reference value with + * the per-pixel stencil value stored in the frame buffer. If the test passes, + * the pixel is written, otherwise the pixel is not written. The stencil + * test function, reference value, and comparison mask are set with the + * setStencilFunction method. The stencil comparison mask is + * bitwise-ANDed with both the stencil reference value and the stored stencil + * value prior to doing the comparison. The default value for the reference value + * is 0. The default value for the comparison mask is ~0 (all ones). + * The stencil test function is one of the following:
  • + *

      + *
    • ALWAYS - pixels are always drawn, irrespective of the stencil + * value. This effectively disables stencil testing. + * This is the default setting.
    • + * + *

    • NEVER - pixels are never drawn, irrespective of the stencil + * value.
    • + * + *

    • EQUAL - pixels are drawn if the stencil reference value is equal + * to the stored stencil value in the frame buffer.
    • + * + *

    • NOT_EQUAL - pixels are drawn if the stencil reference value is + * not equal to the stored stencil value in the frame buffer.
    • + * + *

    • LESS - pixels are drawn if the stencil reference value is less + * than the stored stencil value in the frame buffer.
    • + * + *

    • LESS_OR_EQUAL - pixels are drawn if the stencil reference value + * is less than or equal to the stored stencil value in the frame buffer.
    • + * + *

    • GREATER - pixels are drawn if the stencil reference value is greater + * than the stored stencil value in the frame buffer.
    • + * + *

    • GREATER_OR_EQUAL - pixels are drawn if the stencil reference value + * is greater than or equal to the stored stencil value in the frame buffer.
    • + *

    + * + *
+ * + *

Note: the alpha test, depth test, and stencil functions all use + * the same enums.

+ * + * @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 test function component information. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_DEPTH_TEST_FUNCTION_READ = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_READ; + + /** + * Specifies that this RenderingAttributes object + * allows writing its depth test function component information. + * + * @since Java 3D 1.4 + */ + public static final int + ALLOW_DEPTH_TEST_FUNCTION_WRITE = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_DEPTH_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; + + /** + * Specifies that this RenderingAttributes object allows reading + * its stencil enable, stencil op, stencil function, and + * stencil write mask information. + * + * @since Java 3D 1.4 + */ + public static final int ALLOW_STENCIL_ATTRIBUTES_READ = + CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_READ; + + /** + * Specifies that this RenderingAttributes object allows writing + * its stencil enable, stencil op, stencil function, and + * stencil write mask information. + * + * @since Java 3D 1.4 + */ + public static final int ALLOW_STENCIL_ATTRIBUTES_WRITE = + CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_WRITE; + + + // + // Enums for alpha test, depth test, and stencil test + // + + /** + * Specifies that pixels are always drawn irrespective of the + * values being tested. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * This setting effectively disables alpha, depth, or stencil testing. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int ALWAYS = 0; + + /** + * Specifies that pixels are never drawn irrespective of the + * values being tested. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int NEVER = 1; + + /** + * Specifies that pixels are drawn if the two values being tested are equal. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int EQUAL = 2; + + /** + * Specifies that pixels are drawn if the two values being tested are not equal. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int NOT_EQUAL = 3; + + /** + * Specifies that pixels are drawn if the source/reference value is less + * than the destination/test value. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int LESS = 4; + + /** + * Specifies that pixels are drawn if the source/reference value is less + * than or equal to the destination/test value. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int LESS_OR_EQUAL = 5; + + /** + * Specifies that pixels are drawn if the source/reference value is greater + * than the destination/test value. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int GREATER = 6; + + /** + * Specifies that pixels are drawn if the source/reference value is greater + * than or equal to the destination/test value. + * Can be used to specify the alpha test function, the depth test function, + * or the stencil function. + * + * @see #setAlphaTestFunction + * @see #setDepthTestFunction + * @see #setStencilFunction(int,int,int) + */ + public static final int GREATER_OR_EQUAL = 7; + + + // + // Raster op enums + // + + /** + * Raster operation: DST = 0. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_CLEAR = 0x0; + + /** + * Raster operation: DST = SRC & DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_AND = 0x1; + + /** + * Raster operation: DST = SRC & ~DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_AND_REVERSE = 0x2; + + /** + * Raster operation: DST = SRC. + * @see #setRasterOp + * + * @since Java 3D 1.2 + */ + public static final int ROP_COPY = 0x3; + + /** + * Raster operation: DST = ~SRC & DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_AND_INVERTED = 0x4; + + /** + * Raster operation: DST = DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_NOOP = 0x5; + + /** + * Raster operation: DST = SRC ^ DST. + * @see #setRasterOp + * + * @since Java 3D 1.2 + */ + public static final int ROP_XOR = 0x6; + + /** + * Raster operation: DST = DST | SRC. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_OR = 0x7; + + /** + * Raster operation: DST = ~( DST | SRC ). + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_NOR = 0x8; + + /** + * Raster operation: DST = ~( DST ^ SRC ). + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_EQUIV = 0x9; + + /** + * Raster operation: DST = ~DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_INVERT = 0xA; + + /** + * Raster operation: DST = src | ~DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_OR_REVERSE = 0xB; + + /** + * Raster operation: DST = ~SRC. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_COPY_INVERTED = 0xC; + + /** + * Raster operation: DST = ~SRC | DST. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_OR_INVERTED = 0xD; + + /** + * Raster operation: DST = ~(SRC & DST). + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_NAND = 0xE; + + /** + * Raster operation: DST = 1. + * @see #setRasterOp + * + * @since Java 3D 1.4 + */ + public static final int ROP_SET = 0xF; + + + // + // Stencil op enums + // + + /** + * Stencil operation: DST = DST + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public static final int STENCIL_KEEP = 1; + + /** + * Stencil operation: DST = 0 + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public static final int STENCIL_ZERO = 2; + + /** + * Stencil operation: DST = REF + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public static final int STENCIL_REPLACE = 3; + + /** + * Stencil operation: DST = DST + 1 + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public static final int STENCIL_INCR = 4; + + /** + * Stencil operation: DST = DST - 1 + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public static final int STENCIL_DECR = 5; + + /** + * Stencil operation: DST = ~DST + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public static final int STENCIL_INVERT = 6; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_ALPHA_TEST_FUNCTION_READ, + ALLOW_ALPHA_TEST_VALUE_READ, + ALLOW_DEPTH_ENABLE_READ, + ALLOW_DEPTH_TEST_FUNCTION_READ, + ALLOW_IGNORE_VERTEX_COLORS_READ, + ALLOW_RASTER_OP_READ, + ALLOW_STENCIL_ATTRIBUTES_READ, + ALLOW_VISIBLE_READ + }; + + /** + * Constructs a RenderingAttributes object with default parameters. + * The default values are as follows: + *
    + * depth buffer enable : true
    + * depth buffer write enable : true
    + * alpha test function : ALWAYS
    + * alpha test value : 0.0f
    + * visible : true
    + * ignore vertex colors : false
    + * raster operation enable : false
    + * raster operation : ROP_COPY
    + * depth test: LESS_OR_EQUAL
    + * stencil enable : false
    + * stencil write mask : ~0 (all ones)
    + * stencil op - failOp : STENCIL_KEEP
    + * stencil op - zFailOp : STENCIL_KEEP
    + * stencil op - zPassOp : STENCIL_KEEP
    + * stencil function : ALWAYS
    + * stencil reference value : 0
    + * stencil comparison mask : ~0 (all ones) + *
+ */ + public RenderingAttributes() { + // Just use default attributes + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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_CLEAR, ROP_AND, ROP_AND_REVERSE, ROP_COPY, ROP_AND_INVERTED, + * ROP_NOOP, ROP_XOR, ROP_OR, ROP_NOR, ROP_EQUIV, ROP_INVERT, + * ROP_OR_REVERSE, ROP_COPY_INVERTED, ROP_OR_INVERTED, ROP_NAND or ROP_SET + * + * @since Java 3D 1.2 + */ + public RenderingAttributes(boolean depthBufferEnable, + boolean depthBufferWriteEnable, + float alphaTestValue, + int alphaTestFunction, + boolean visible, + boolean ignoreVertexColors, + boolean rasterOpEnable, + int rasterOp) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * + * @see GraphicsConfigTemplate3D#setDepthSize + */ + 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 + * each incoming (source) per-pixel alpha value with the alpha test 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 + * 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 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_CLEAR, ROP_AND, ROP_AND_REVERSE, ROP_COPY, ROP_AND_INVERTED, + * ROP_NOOP, ROP_XOR, ROP_OR, ROP_NOR, ROP_EQUIV, ROP_INVERT, + * ROP_OR_REVERSE, ROP_COPY_INVERTED, ROP_OR_INVERTED, ROP_NAND or ROP_SET. + * @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_CLEAR, ROP_AND, ROP_AND_REVERSE, ROP_COPY, ROP_AND_INVERTED, + * ROP_NOOP, ROP_XOR, ROP_OR, ROP_NOR, ROP_EQUIV, ROP_INVERT, + * ROP_OR_REVERSE, ROP_COPY_INVERTED, ROP_OR_INVERTED, ROP_NAND or ROP_SET + * @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. + */ + @Override + void createRetained() { + this.retained = new RenderingAttributesRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + RenderingAttributes ra = new RenderingAttributes(); + ra.duplicateNodeComponent(this); + return ra; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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.initDepthTestFunction(attr.getDepthTestFunction()); + rt.initAlphaTestValue(attr.getAlphaTestValue()); + rt.initAlphaTestFunction(attr.getAlphaTestFunction()); + rt.initVisible(attr.getVisible()); + rt.initIgnoreVertexColors(attr.getIgnoreVertexColors()); + rt.initRasterOpEnable(attr.getRasterOpEnable()); + rt.initRasterOp(attr.getRasterOp()); + rt.initStencilEnable(attr.getStencilEnable()); + int[] ops = new int[3]; + attr.getStencilOp(ops); + rt.initStencilOp(ops[0], ops[1], ops[2]); + attr.getStencilFunction(ops); + rt.initStencilFunction(ops[0], ops[1], ops[2]); + rt.initStencilWriteMask(attr.getStencilWriteMask()); + + } + + /** + * Set depth test function. This function is used to compare each + * incoming (source) per-pixel depth test value with the stored per-pixel + * depth value in the frame buffer. If the test + * passes, the pixel is written, otherwise the pixel is not + * written. + * @param function the new depth test function. One of + * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, + * or GREATER_OR_EQUAL. + * The default value is LESS_OR_EQUAL. + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public void setDepthTestFunction(int function){ + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_DEPTH_TEST_FUNCTION_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes14")); + + if (isLive()) + ((RenderingAttributesRetained)this.retained).setDepthTestFunction(function); + else + ((RenderingAttributesRetained)this.retained).initDepthTestFunction(function); + } + + /** + * Retrieves current depth test function. + * @return the current depth test function + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public int getDepthTestFunction(){ + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_DEPTH_TEST_FUNCTION_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes15")); + + return ((RenderingAttributesRetained)this.retained).getDepthTestFunction(); + } + + /** + * Enables or disables the stencil buffer for this RenderingAttributes + * component object. If the stencil buffer is disabled, the + * stencil operation and function are ignored. If a scene graph + * is rendered on a Canvas3D that does not have a stencil buffer, + * the stencil buffer will be implicitly disabled for that canvas. + * + * @param state true or false to enable or disable stencil buffer + * operations. + * If this is set to false, the stencilOp and stencilFunction parameters + * are not used. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @see GraphicsConfigTemplate3D#setStencilSize + * + * @since Java 3D 1.4 + */ + public void setStencilEnable(boolean state) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16")); + } + } + + if (isLive()) + ((RenderingAttributesRetained)this.retained).setStencilEnable(state); + else + ((RenderingAttributesRetained)this.retained).initStencilEnable(state); + + } + + /** + * Retrieves the stencil buffer enable flag for this RenderingAttributes + * object. + * + * @return true if stencil buffer operations are enabled; false + * if stencil buffer 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.4 + */ + public boolean getStencilEnable() { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17")); + } + } + + return ((RenderingAttributesRetained)this.retained).getStencilEnable(); + } + + /** + * Sets the stencil operations for this RenderingAttributes object to the + * specified parameters. + * + * @param failOp operation performed when the stencil test fails, one of: + * STENCIL_KEEP, STENCIL_ZERO, STENCIL_REPLACE, STENCIL_INCR, STENCIL_DECR, + * or STENCIL_INVERT. + * + * @param zFailOp operation performed when the stencil test passes and the + * depth test fails, one of: + * STENCIL_KEEP, STENCIL_ZERO, STENCIL_REPLACE, STENCIL_INCR, STENCIL_DECR, + * or STENCIL_INVERT. + * + * @param zPassOp operation performed when both the stencil test and the + * depth test pass, one of: + * STENCIL_KEEP, STENCIL_ZERO, STENCIL_REPLACE, STENCIL_INCR, STENCIL_DECR, + * or STENCIL_INVERT. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public void setStencilOp(int failOp, int zFailOp, int zPassOp) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16")); + } + } + + if (isLive()) + ((RenderingAttributesRetained)this.retained).setStencilOp(failOp, + zFailOp, + zPassOp); + else + ((RenderingAttributesRetained)this.retained).initStencilOp(failOp, + zFailOp, + zPassOp); + + } + + /** + * Sets the stencil operations for this RenderingAttributes object to the + * specified parameters. + * + * @param stencilOps an array of three integers that specifies the new + * set of stencil operations. Element 0 of the array specifies the + * failOp parameter, element 1 specifies the + * zFailOp parameter, and element 2 specifies the + * zPassOp parameter. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @see #setStencilOp(int,int,int) + * + * @since Java 3D 1.4 + */ + public void setStencilOp(int[] stencilOps) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16")); + } + } + + if (isLive()) + ((RenderingAttributesRetained)this.retained).setStencilOp(stencilOps[0], + stencilOps[1], + stencilOps[2]); + else + ((RenderingAttributesRetained)this.retained).initStencilOp(stencilOps[0], + stencilOps[1], + stencilOps[2]); + } + + /** + * Retrieves the current set of stencil operations, and copies them + * into the specified array. The caller must ensure that this array + * has been allocated with enough space to hold the results. + * + * @param stencilOps array that will receive the current set of + * three stencil operations. The failOp parameter is copied + * into element 0 of the array, the zFailOp parameter is copied + * into element 1, and the zPassOp parameter is copied + * into element 2. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public void getStencilOp(int[] stencilOps) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17")); + } + } + + ((RenderingAttributesRetained)this.retained).getStencilOp(stencilOps); + } + + /** + * Sets the stencil function, reference value, and comparison mask + * for this RenderingAttributes object to the specified parameters. + * + * @param function the stencil test function, used to compare the + * stencil reference value with the stored per-pixel + * stencil value in the frame buffer. If the test + * passes, the pixel is written, otherwise the pixel is not + * written. The stencil function is one of: + * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, + * or GREATER_OR_EQUAL. + * + * @param refValue the stencil reference value that is tested against + * the stored per-pixel stencil value + * + * @param compareMask a mask that limits which bits are compared; it is + * bitwise-ANDed with both the stencil reference value and the stored + * per-pixel stencil value before doing the comparison. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public void setStencilFunction(int function, int refValue, int compareMask) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16")); + } + } + if (isLive()) + ((RenderingAttributesRetained)this.retained).setStencilFunction(function, + refValue, + compareMask); + else + ((RenderingAttributesRetained)this.retained).initStencilFunction(function, + refValue, + compareMask); + } + + /** + * Sets the stencil function, reference value, and comparison mask + * for this RenderingAttributes object to the specified parameters. + * + * @param params an array of three integers that specifies the new + * stencil function, reference value, and comparison mask. + * Element 0 of the array specifies the + * stencil function, element 1 specifies the + * reference value, and element 2 specifies the + * comparison mask. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @see #setStencilFunction(int,int,int) + * + * @since Java 3D 1.4 + */ + public void setStencilFunction(int[] params) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16")); + } + } + + if (isLive()) + ((RenderingAttributesRetained)this.retained).setStencilFunction(params[0], + params[1], + params[2]); + else + ((RenderingAttributesRetained)this.retained).initStencilFunction(params[0], + params[1], + params[2]); + + } + + /** + * Retrieves the stencil function, reference value, and comparison mask, + * and copies them into the specified array. The caller must ensure + * that this array has been allocated with enough space to hold the results. + * + * @param params array that will receive the current stencil function, + * reference value, and comparison mask. The stencil function is copied + * into element 0 of the array, the reference value is copied + * into element 1, and the comparison mask is copied + * into element 2. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public void getStencilFunction(int[] params) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17")); + } + } + + ((RenderingAttributesRetained)this.retained).getStencilFunction(params); + } + + /** + * Sets the stencil write mask for this RenderingAttributes + * object. This mask controls which bits of the + * stencil buffer are written. + * The default value is ~0 (all ones). + * + * @param mask the new stencil write mask. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public void setStencilWriteMask(int mask) { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16")); + } + } + + if (isLive()) + ((RenderingAttributesRetained)this.retained).setStencilWriteMask(mask); + else + ((RenderingAttributesRetained)this.retained).initStencilWriteMask(mask); + } + + /** + * Retrieves the current stencil write mask for this RenderingAttributes + * object. + * + * @return the stencil write mask. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @since Java 3D 1.4 + */ + public int getStencilWriteMask() { + if (isLiveOrCompiled()) { + if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) { + throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17")); + } + } + + return ((RenderingAttributesRetained)this.retained).getStencilWriteMask(); + } + +} diff --git a/src/javax/media/j3d/RenderingAttributesRetained.java b/src/javax/media/j3d/RenderingAttributesRetained.java new file mode 100644 index 0000000..5f67c54 --- /dev/null +++ b/src/javax/media/j3d/RenderingAttributesRetained.java @@ -0,0 +1,729 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + static final int DEPTH_TEST_FUNC = 0x100; + + static final int STENCIL_ENABLE = 0x200; + + static final int STENCIL_OP_VALUES = 0x400; + + static final int STENCIL_FUNC = 0x800; + + static final int STENCIL_WRITE_MASK = 0x1000; + + // depth buffer Enable for hidden surface removal + boolean depthBufferEnable = true; + + boolean depthBufferWriteEnable = true; + + float alphaTestValue = 0.0f; + + int alphaTestFunction = RenderingAttributes.ALWAYS; + + int depthTestFunction = RenderingAttributes.LESS_OR_EQUAL; + + boolean visible = true; + + boolean ignoreVertexColors = false; + + // raster operation + boolean rasterOpEnable = false; + int rasterOp = RenderingAttributes.ROP_COPY; + + // stencil operation + boolean stencilEnable = false; + int stencilFailOp = RenderingAttributes.STENCIL_KEEP; + int stencilZFailOp = RenderingAttributes.STENCIL_KEEP; + int stencilZPassOp = RenderingAttributes.STENCIL_KEEP; + int stencilFunction = RenderingAttributes.ALWAYS; + int stencilReferenceValue = 0; + int stencilCompareMask = ~0; + int stencilWriteMask = ~0; + + // depth buffer comparison function. Used by multi-texturing only + //[PEPE] NOTE: they are both unused. Candidates for removal. + 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 + */ + 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 + */ + 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 + */ + final boolean getVisible() { + return visible; + } + + + /** + * Enables or disables vertex colors for this RenderAttributes + * component object. + * @param state true or false to enable or disable vertex colors + */ + final void initIgnoreVertexColors(boolean state) { + ignoreVertexColors = state; + } + + /** + * Enables or disables vertex colors 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 vertex colors + */ + final void setIgnoreVertexColors(boolean state) { + initIgnoreVertexColors(state); + sendMessage(IGNORE_VCOLOR, + (state ? Boolean.TRUE: Boolean.FALSE)); + } + + /** + * Retrieves the state of vertex color Enable flag + * @return true if vertex colors are enabled, false + * if vertex colors are disabled + */ + 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; + } + + /** + * Set depth test function. This function is used to compare the + * depth 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 depth test function. One of: + * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, + * GREATER_OR_EQUAL. + * Default value is LESS_OR_EQUAL + */ + final void initDepthTestFunction(int function){ + depthTestFunction = function; + } + + /** + * Set depth test function. This function is used to compare the + * depth test value with each per-pixel depth value. If the test + * passes, the pixel is written otherwise the pixel is not + * written. + * @param function the new depth test function. One of + * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, + * GREATER_OR_EQUAL + * Default value is LESS_OR_EQUAL + */ + final void setDepthTestFunction(int function){ + initDepthTestFunction(function); + sendMessage(DEPTH_TEST_FUNC, new Integer(function)); + } + + /** + * Retrieves current depth test function. + * @return the current depth test function + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + final int getDepthTestFunction(){ + return depthTestFunction; + } + + /** + * 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; + } + + + // Stencil operations + /** + * Initialize the stencil enable state + */ + final void initStencilEnable(boolean state) { + stencilEnable = state; + } + + /** + * Set the stencil enable state + */ + final void setStencilEnable(boolean state) { + initStencilEnable(state); + sendMessage(STENCIL_ENABLE, new Boolean(state)); + } + + /** + * Retrieves the current stencil enable state. + */ + final boolean getStencilEnable() { + return stencilEnable; + } + + /** + * Initialize the stencil op. value + */ + final void initStencilOp(int failOp, int zFailOp, int zPassOp) { + stencilFailOp = failOp; + stencilZFailOp = zFailOp; + stencilZPassOp = zPassOp; + } + + /** + * Set the stencil op. value + */ + final void setStencilOp(int failOp, int zFailOp, int zPassOp) { + initStencilOp(failOp, zFailOp, zPassOp); + + ArrayList arrList = new ArrayList(3); + arrList.add(new Integer(failOp)); + arrList.add(new Integer(zFailOp)); + arrList.add(new Integer(zPassOp)); + sendMessage(STENCIL_OP_VALUES, arrList); + } + + /** + * Retrieves the current stencil op. value + */ + final void getStencilOp(int[] stencilOps) { + stencilOps[0] = stencilFailOp; + stencilOps[1] = stencilZFailOp; + stencilOps[2] = stencilZPassOp; + } + + + /** + * Initialize the stencil function value + */ + final void initStencilFunction(int function, int refValue, int compareMask) { + stencilFunction = function; + stencilReferenceValue = refValue; + stencilCompareMask = compareMask; + } + + /** + * Set the stencil function value + */ + final void setStencilFunction(int function, int refValue, int compareMask) { + initStencilOp(function, refValue, compareMask); + + ArrayList arrList = new ArrayList(3); + arrList.add(new Integer(function)); + arrList.add(new Integer(refValue)); + arrList.add(new Integer(compareMask)); + sendMessage(STENCIL_FUNC, arrList); + } + + /** + * Retrieves the current stencil op. value + */ + final void getStencilFunction(int[] params) { + params[0] = stencilFunction; + params[1] = stencilReferenceValue; + params[2] = stencilCompareMask; + } + + + /** + * Initialize the stencil write mask + */ + final void initStencilWriteMask(int mask) { + stencilWriteMask = mask; + } + + /** + * Set the stencil write mask + */ + final void setStencilWriteMask(int mask) { + initStencilWriteMask(mask); + sendMessage(STENCIL_WRITE_MASK, new Integer(mask)); + } + + /** + * Retrieves the current stencil write mask + */ + final int getStencilWriteMask() { + return stencilWriteMask; + } + + + /** + * Updates the native context. + */ + + /** + * Updates the native context. + */ + void updateNative(Canvas3D c3d, + boolean depthBufferWriteEnableOverride, + boolean depthBufferEnableOverride) { + Pipeline.getPipeline().updateRenderingAttributes(c3d.ctx, + depthBufferWriteEnableOverride, depthBufferEnableOverride, + depthBufferEnable, depthBufferWriteEnable, depthTestFunction, + alphaTestValue, alphaTestFunction, ignoreVertexColors, + rasterOpEnable, rasterOp, c3d.userStencilAvailable, stencilEnable, + stencilFailOp, stencilZFailOp, stencilZPassOp, + stencilFunction, stencilReferenceValue, stencilCompareMask, + stencilWriteMask ); + } + + /** + * Creates and initializes a mirror object, point the mirror object + * to the retained object if the object is not editable + */ + @Override + 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 + */ + @Override + synchronized void initMirrorObject() { + ((RenderingAttributesRetained)mirror).set(this); + } + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 & DEPTH_TEST_FUNC) != 0) { + mirrorRa.depthTestFunction = ((Integer)value).intValue(); + } + 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()); + } + else if ((component & STENCIL_ENABLE) != 0) { + mirrorRa.stencilEnable = (((Boolean)value).booleanValue()); + } + else if ((component & STENCIL_OP_VALUES) != 0) { + ArrayList arrlist = (ArrayList) value; + mirrorRa.stencilFailOp = (((Integer)arrlist.get(0)).intValue()); + mirrorRa.stencilZFailOp = (((Integer)arrlist.get(1)).intValue()); + mirrorRa.stencilZPassOp = (((Integer)arrlist.get(2)).intValue()); + } + else if ((component & STENCIL_FUNC) != 0) { + ArrayList arrlist = (ArrayList) value; + mirrorRa.stencilFunction = (((Integer)arrlist.get(0)).intValue()); + mirrorRa.stencilReferenceValue = (((Integer)arrlist.get(1)).intValue()); + mirrorRa.stencilCompareMask = (((Integer)arrlist.get(2)).intValue()); + } + else if ((component & STENCIL_WRITE_MASK) != 0) { + mirrorRa.stencilWriteMask = (((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) && + (rr.depthTestFunction == depthTestFunction) && + (rr.stencilEnable == stencilEnable) && + (rr.stencilFailOp == stencilFailOp) && + (rr.stencilZFailOp == stencilZFailOp) && + (rr.stencilZPassOp == stencilZPassOp) && + (rr.stencilFunction == stencilFunction) && + (rr.stencilReferenceValue == stencilReferenceValue) && + (rr.stencilCompareMask == stencilCompareMask) && + (rr.stencilWriteMask == stencilWriteMask)); + } + + protected void set(RenderingAttributesRetained ra) { + super.set(ra); + depthBufferEnable = ra.depthBufferEnable; + depthBufferWriteEnable = ra.depthBufferWriteEnable; + alphaTestValue = ra.alphaTestValue; + alphaTestFunction = ra.alphaTestFunction; + depthTestFunction = ra.depthTestFunction; + visible = ra.visible; + ignoreVertexColors = ra.ignoreVertexColors; + rasterOpEnable = ra.rasterOpEnable; + rasterOp = ra.rasterOp; + stencilEnable = ra.stencilEnable; + stencilFailOp = ra.stencilFailOp; + stencilZFailOp = ra.stencilZFailOp; + stencilZPassOp = ra.stencilZPassOp; + stencilFunction = ra.stencilFunction; + stencilReferenceValue = ra.stencilReferenceValue; + stencilCompareMask = ra.stencilCompareMask; + stencilWriteMask = ra.stencilWriteMask; + + } + + 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 = new J3dMessage(); + 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.err.println("changedFreqent1 = "+changedFrequent); + createMessage.args[3] = new Integer(changedFrequent); + VirtualUniverse.mc.processMessage(createMessage); + + // System.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + // TODO : Need to handle stencil operation -- Chien + @Override + 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( bit == RenderingAttributes.ALLOW_DEPTH_TEST_FUNCTION_WRITE) + mask = DEPTH_TEST_FUNC; + + if( bit == RenderingAttributes.ALLOW_STENCIL_ATTRIBUTES_WRITE) + mask = DEPTH_TEST_FUNC; + + if( bit == RenderingAttributes.ALLOW_DEPTH_TEST_FUNCTION_WRITE) + mask = STENCIL_ENABLE | STENCIL_OP_VALUES | STENCIL_FUNC | + STENCIL_WRITE_MASK; + + if (mask != 0) + setFrequencyChangeMask(bit, mask); + // System.err.println("changedFreqent2 = "+changedFrequent); + } + +} diff --git a/src/javax/media/j3d/RenderingAttributesStructure.java b/src/javax/media/j3d/RenderingAttributesStructure.java new file mode 100644 index 0000000..67ba1ef --- /dev/null +++ b/src/javax/media/j3d/RenderingAttributesStructure.java @@ -0,0 +1,248 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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); + } + + @Override + 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) { + // Appearance is always updated immediately, since rBin needs + // the most up-to-date values for restructuring + case J3dMessage.APPEARANCE_CHANGED: + case J3dMessage.SHADER_APPEARANCE_CHANGED: + case J3dMessage.TEXTURE_UNIT_STATE_CHANGED: + { + // System.err.println("1 RAS : J3dMessage type : " + m.type); + 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: + case J3dMessage.SHADER_ATTRIBUTE_CHANGED: + case J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED: + { + // System.err.println("2 RAS : J3dMessage type : " + m.type); + + 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]; + 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); + } + + @Override + public void updateObject() { + + int size = objList.size(); + for (int i = 0; i < size; i++) { + J3dMessage m = 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.SHADER_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(); + } + + + private void updateNodeComponent(Object[] args) { + // System.err.println("RAS : updateNodeComponent : " + this); + NodeComponentRetained n = (NodeComponentRetained)args[0]; + n.updateMirrorObject(((Integer)args[1]).intValue(), args[2]); + } + + private void updateTextureAttributes(Object[] args) { + TextureAttributesRetained n = (TextureAttributesRetained)args[0]; + n.updateMirrorObject(((Integer)args[1]).intValue(), args[2], args[3]); + } + + @Override + void removeNodes(J3dMessage m) {} + + @Override + void cleanup() {} + + +} + + diff --git a/src/javax/media/j3d/RenderingEnvironmentStructure.java b/src/javax/media/j3d/RenderingEnvironmentStructure.java new file mode 100644 index 0000000..a7e55f2 --- /dev/null +++ b/src/javax/media/j3d/RenderingEnvironmentStructure.java @@ -0,0 +1,1746 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import javax.vecmath.Vector3d; + +/** + * 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(); + + // 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 +private final 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() { + int size = objFreeList.size(); + if (size == 0) + return new Object[5]; + + return objFreeList.remove(size - 1); +} + +void addObjArrayToFreeList(Object[] objs) { + Arrays.fill(objs, null); + objFreeList.add(objs); +} + +@Override +public void updateObject() { + int size; + + size = objList.size(); + for (int i = 0; i < size; i++) { + Object[] args = objList.get(i); + LeafRetained leaf = (LeafRetained)args[0]; + leaf.updateMirrorObject(args); + addObjArrayToFreeList(args); + } + objList.clear(); + + size = xformChangeList.size(); + for (int i = 0; i < size; i++) { + xformChangeList.get(i).updateTransformChange(); + } + xformChangeList.clear(); +} + + @Override + 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(m.args); + break; + case J3dMessage.BOUNDINGLEAF_CHANGED: + updateBoundingLeaf(m.args); + break; + case J3dMessage.FOG_CHANGED: + updateFog(m.args); + break; + case J3dMessage.ALTERNATEAPPEARANCE_CHANGED: + updateAltApp(m.args); + break; + case J3dMessage.SHAPE3D_CHANGED: + updateShape3D(m.args); + break; + case J3dMessage.ORIENTEDSHAPE3D_CHANGED: + updateOrientedShape3D(m.args); + break; + case J3dMessage.MORPH_CHANGED: + updateMorph(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(m.args); + break; + case J3dMessage.BACKGROUND_CHANGED: + updateBackground(m.args); + break; + case J3dMessage.CLIP_CHANGED: + updateClip(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; + + 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; + + 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 viewScopedNodes = (ArrayList)m.args[3]; + ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; + int i; + + for (i=0; i vl = scopedNodesViewList.get(i); + if (n instanceof LightRetained) { + LightRetained lt = (LightRetained) n; + lt.isViewScoped = true; + numberOfLights++; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedLights.get(view); + if (list == null) { + list = new ArrayList(); + viewScopedLights.put(view, list); + } + list.add(lt); + } + } + else if (n instanceof FogRetained) { + FogRetained ft = (FogRetained) n; + ft.isViewScoped = true; + numberOfFogs++; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedFogs.get(view); + if (list == null) { + list = new ArrayList(); + viewScopedFogs.put(view, list); + } + list.add(ft); + } + } + else if (n instanceof AlternateAppearanceRetained) { + AlternateAppearanceRetained aart = (AlternateAppearanceRetained) n; + aart.isViewScoped = true; + numberOfAltApps++; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedAltAppearances + .get(view); + if (list == null) { + list = new ArrayList(); + viewScopedAltAppearances.put(view, list); + } + list.add(aart); + } + } + else if (n instanceof BackgroundRetained) { + BackgroundRetained bt = (BackgroundRetained) n; + bt.isViewScoped = true; + numberOfBgs++; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedBackgrounds + .get(view); + if (list == null) { + list = new ArrayList(); + viewScopedBackgrounds.put(view, list); + } + list.add(bt); + } + } + else if (n instanceof ClipRetained) { + ClipRetained ct = (ClipRetained) n; + ct.isViewScoped = true; + numberOfClips++; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedClips.get(view); + if (list == null) { + list = new ArrayList(); + viewScopedClips.put(view, list); + } + list.add(ct); + } + } else if (n instanceof ModelClipRetained) { + ModelClipRetained mt = (ModelClipRetained)n; + mt.isViewScoped = true; + numberOfModelClips++; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedModelClips.get(view); + if (list == null) { + list = new ArrayList(); + viewScopedModelClips.put(view, list); + } + list.add(mt); + } + } + } + + } + 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]; + } + + @Override + void removeNodes(J3dMessage m) { + Object[] nodes = (Object[])m.args[0]; + ArrayList viewScopedNodes = (ArrayList)m.args[3]; + ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; + int i; + GeometryAtom ga; + LeafRetained oldsrc = null; + + // System.err.println("RE : removeNodes message " + m); + // System.err.println("RE : removeNodes m.args[0] " + m.args[0]); + + for (i=0; i vl = scopedNodesViewList.get(i); + if (n instanceof LightRetained) { + LightRetained lt = (LightRetained) n; + lt.isViewScoped = false; + numberOfLights--; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedLights.get(view); + list.remove(lt); + if (list.size() == 0) { + viewScopedLights.remove(view); + } + } + } + else if (n instanceof FogRetained) { + FogRetained ft = (FogRetained)n; + ft.isViewScoped = false; + numberOfFogs--; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedFogs.get(view); + list.remove(ft); + if (list.size() == 0) { + viewScopedFogs.remove(view); + } + } + } else if (n instanceof AlternateAppearanceRetained) { + AlternateAppearanceRetained aart = (AlternateAppearanceRetained) n; + aart.isViewScoped = false; + numberOfAltApps--; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedAltAppearances + .get(view); + list.remove(aart); + if (list.size() == 0) { + viewScopedAltAppearances.remove(view); + } + } + } + else if (n instanceof BackgroundRetained) { + BackgroundRetained bt = (BackgroundRetained)n; + bt.isViewScoped = false; + numberOfBgs--; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedBackgrounds.get(view); + list.remove(bt); + if (list.size() == 0) { + viewScopedBackgrounds.remove(view); + } + } + } + else if (n instanceof ClipRetained) { + ClipRetained ct = (ClipRetained) n; + ct.isViewScoped = false; + numberOfClips--; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedClips.get(view); + list.remove(ct); + if (list.size() == 0) { + viewScopedClips.remove(view); + } + } + } else if (n instanceof ModelClipRetained) { + ModelClipRetained mt = (ModelClipRetained)n; + mt.isViewScoped = false; + numberOfModelClips--; + vlsize = vl.size(); + for (int k = 0; k < vlsize; k++) { + View view = vl.get(k); + ArrayList list = viewScopedModelClips.get(view); + list.remove(mt); + if (list.size() == 0) { + viewScopedModelClips.remove(view); + } + } + } + } + + } + } + + LightRetained[] getInfluencingLights(RenderAtom ra, View view) { + LightRetained[] lightAry = null; + int i, j; + + // Need to lock retlights, since on a multi-processor + // system with 2 views on a single universe, there might + // be councurrent access + synchronized (retlights) { + ArrayList globalLights; + int numLights = 0; + if (ra.geometryAtom.source.inBackgroundGroup) { + globalLights = ra.geometryAtom.source.geometryBackground.lights; + numLights = processLights(globalLights, ra, numLights); + } + else { + if ((globalLights = 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 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 globalBgs, BoundingSphere bounds, int nbacks, Locale viewLocale) { + int size = globalBgs.size(); + + for (int i = 0; i < size; i++) { + BackgroundRetained back = globalBgs.get(i); + if (back.transformedRegion == null || !back.switchState.currentSwitchOn) + continue; + + if (back.cachedLocale != viewLocale) { + Bounds 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 j; + Bounds closestBounds; + boolean backClipActive; + double[] backClipDistance; + double distance; + + // 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; + int nclips = 0; + distance = 0.0; + if (intersectedBounds.length < numberOfClips) + intersectedBounds = new Bounds[numberOfClips]; + + ArrayList globalClips = viewScopedClips.get(view); + if (globalClips != 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 size = globalClips.size(); + + for (int i = 0; i < size; i++) { + ClipRetained clip = globalClips.get(i); + if (clip.transformedRegion != null && + 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; + 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) { + LightRetained ml = (LightRetained)nodes[i]; + ml.updateImmediateTransformChange(); + xformChangeList.add(ml); + + } else if (nodes[i] instanceof FogRetained) { + FogRetained mfog = (FogRetained) nodes[i]; + mfog.updateImmediateTransformChange(); + xformChangeList.add(mfog); + + } else if (nodes[i] instanceof AlternateAppearanceRetained){ + AlternateAppearanceRetained mAltApp = + (AlternateAppearanceRetained) nodes[i]; + mAltApp.updateImmediateTransformChange(); + xformChangeList.add(mAltApp); + + } 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) { + int j; + Bounds closestBounds; + Bounds bounds; + Object[] retVal; + 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) { + int nAltApp = 0; + bounds = ra.localeVwcBounds; + + if (intersectedBounds.length < numberOfAltApps) + intersectedBounds = new Bounds[numberOfAltApps]; + + ArrayList globalAltApps = viewScopedAltAppearances.get(view); + if (globalAltApps != null) + nAltApp = processAltApps(globalAltApps, ra, nAltApp); + + nAltApp = processAltApps(nonViewScopedAltAppearances, ra, nAltApp); + AlternateAppearanceRetained 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(); + Bounds bounds = ra.localeVwcBounds; + AlternateAppearanceRetained[] shapeScopedAltApp; + + if (size == 0) + return nAltApp; + + for (int i = 0; i < size; i++) { + AlternateAppearanceRetained altApp = globalAltApps.get(i); + // System.err.println("altApp.region = "+altApp.region+" altApp.switchState.currentSwitchOn = "+altApp.switchState.currentSwitchOn+" intersect = "+altApp.region.intersect(ra.geometryAtom.vwcBounds)); + // System.err.println("altApp.isScoped = "+altApp.isScoped); + // Note : There is no enable check for fog + if (altApp.region == null || !altApp.switchState.currentSwitchOn) + continue; + + if (altApp.region.intersect(bounds) == true) { + int n = ra.geometryAtom.source.numAltApps; + shapeScopedAltApp = ra.geometryAtom.source.altApps; + if (altApp.isScoped) { + for (int 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.err.println("===> non null Vsg"); + int size = vsgs.size(); + for (int i = 0; i < size; i++) { + ViewSpecificGroupRetained v = vsgs.get(i); + ArrayList l = vlists.get(i); + int index = keys[i]; + // System.err.println("v = "+v+" index = "+index+" l = "+l); + v.cachedViewList.add(index, l); + /* + for (int k = 0; k < v.cachedViewList.size(); k++) { + System.err.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 = vsgs.get(i); + int index = keys[i]; + if (index == -1) { + int csize = v.cachedViewList.size(); + for (int j = 0; j < csize; j++) { + v.cachedViewList.get(j).clear(); + } + v.cachedViewList.clear(); + } + else { + v.cachedViewList.remove(index).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; + + 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 lt = (LightRetained)obj; + lt.isViewScoped = true; + numberOfLights++; + if (ltList == null) { + if ((ltList = viewScopedLights.get(view)) == null) { + ltList = new ArrayList(); + viewScopedLights.put(view, ltList); + } + } + ltList.add(lt); + } + if (obj instanceof FogRetained) { + FogRetained ft = (FogRetained)obj; + ft.isViewScoped = true; + numberOfFogs++; + if (fogList == null) { + if ((fogList = viewScopedFogs.get(view)) == null) { + fogList = new ArrayList(); + viewScopedFogs.put(view, fogList); + } + } + fogList.add(ft); + } + if (obj instanceof ModelClipRetained) { + ModelClipRetained mc = (ModelClipRetained)obj; + mc.isViewScoped = true; + numberOfModelClips++; + if (mclipList == null) { + if ((mclipList = viewScopedModelClips.get(view)) == null) { + mclipList = new ArrayList(); + viewScopedModelClips.put(view, mclipList); + } + } + mclipList.add(mc); + } + if (obj instanceof AlternateAppearanceRetained) { + AlternateAppearanceRetained aart = (AlternateAppearanceRetained)obj; + aart.isViewScoped = true; + numberOfAltApps++; + if (altAppList == null) { + if ((altAppList = viewScopedAltAppearances + .get(view)) == null) { + altAppList = new ArrayList(); + viewScopedAltAppearances.put(view, altAppList); + } + } + altAppList.add(aart); + } + if (obj instanceof ClipRetained) { + ClipRetained ct = (ClipRetained) obj; + ct.isViewScoped = true; + numberOfClips++; + if (clipList == null) { + if ((clipList = viewScopedClips.get(view)) == null) { + clipList = new ArrayList(); + viewScopedClips.put(view, clipList); + } + } + clipList.add(ct); + } + if (obj instanceof BackgroundRetained) { + BackgroundRetained bg = (BackgroundRetained) obj; + bg.isViewScoped = true; + numberOfBgs++; + if (bgList == null) { + if ((bgList = viewScopedBackgrounds.get(view)) == null) { + bgList = new ArrayList(); + viewScopedBackgrounds.put(view, bgList); + } + } + bgList.add(bg); + } + } + 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 = viewScopedLights.get(view); + } + ltList.remove(obj); + } + if (obj instanceof FogRetained) { + ((FogRetained)obj).isViewScoped = false; + numberOfFogs--; + if (fogList == null) { + fogList = viewScopedFogs.get(view); + } + fogList.remove(obj); + } + if (obj instanceof ModelClipRetained) { + ((ModelClipRetained)obj).isViewScoped = false; + numberOfModelClips--; + if (mclipList == null) { + mclipList = viewScopedModelClips.get(view); + } + mclipList.remove(obj); + } + if (obj instanceof AlternateAppearanceRetained) { + ((AlternateAppearanceRetained)obj).isViewScoped = false; + numberOfAltApps--; + if (altAppList == null) { + altAppList = viewScopedAltAppearances.get(view); + } + altAppList.remove(obj); + } + if (obj instanceof ClipRetained) { + ((ClipRetained)obj).isViewScoped = false; + numberOfClips--; + if (clipList == null) { + clipList = viewScopedClips.get(view); + } + clipList.remove(obj); + } + if (obj instanceof BackgroundRetained) { + ((BackgroundRetained)obj).isViewScoped = false; + numberOfBgs++; + if (bgList == null) { + bgList = 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 = 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 = 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 = 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 = 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 = 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 = viewScopedModelClips.get(view); + // If this is a scoped mclip, but has no views then .. + if (l == null || !l.contains(mclip)) + return false; + } + return true; +} + +@Override +void cleanup() {} +} diff --git a/src/javax/media/j3d/RenderingError.java b/src/javax/media/j3d/RenderingError.java new file mode 100644 index 0000000..da68dd9 --- /dev/null +++ b/src/javax/media/j3d/RenderingError.java @@ -0,0 +1,268 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.GraphicsDevice; +import java.io.PrintStream; + +/** + * RenderingError is a container object that holds the details of + * a runtime error that occurs in the Java 3D rendering system. + * + * @since Java 3D 1.5 + */ +public class RenderingError extends Object { + private int errorCode = NO_ERROR; + private String errorMessage = null; + private String detailMessage = null; + private GraphicsDevice graphicsDevice = null; + private Canvas3D canvas = null; + + /** + * Indicates that no error occurred. + */ + public static final int NO_ERROR = 0; + + /** + * Indicates that an unexpected rendering exception was caught by the + * Java 3D renderer thread. + */ + public static final int UNEXPECTED_RENDERING_ERROR = 1; + + /** + * Indicates that an error occurred while getting the best graphics + * configuration or while testing whether a given graphics config is + * supported. + */ + public static final int GRAPHICS_CONFIG_ERROR = 2; + + /** + * Indicates that an error occurred while creating an OpenGL or D3D + * graphics context. This can happen either when querying + * the Canvas3D properties or when rendering. + */ + public static final int CONTEXT_CREATION_ERROR = 3; + + /** + * Indicates a error in creating a rendering buffer for an off-screen + * Canvas3D. + */ + public static final int OFF_SCREEN_BUFFER_ERROR = 4; + + + /** + * Constructs a new RenderingError object indicating no error. The + * error code is set to NO_ERROR. All other fields + * are initialized to null, including the error message. + */ + public RenderingError() { + } + + /** + * Constructs a new RenderingError object with the given error code + * and message. All other fields are initialized to null. + * + * @param errorCode the error code for this rendering error. + * @param errorMessage a short error message describing this + * rendering error. + */ + public RenderingError(int errorCode, String errorMessage) { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + /** + * Prints a verbose error report to System.err. This verbose + * output includes the error code, error message, detail message, + * and all relevant Java 3D objects. + */ + public void printVerbose() { + printVerbose(System.err); + } + + /** + * Prints a verbose error report to the specified PrintStream. + * This verbose output includes the error code, error message, + * detail message, and all relevant Java 3D objects. + * + * @param printStream the print stream on which to print the error + * report. + */ + public void printVerbose(PrintStream printStream) { + printStream.println(this); + if (graphicsDevice != null) { + printStream.println("graphicsDevice = " + graphicsDevice); + } + if (canvas != null) { + printStream.println("canvas = " + canvas); + } + + if (detailMessage != null) { + printStream.println(); + printStream.println("Detail Message"); + printStream.println("--------------"); + printStream.println(detailMessage); + } + } + + /** + * Sets the error code for this rendering error. This represents the + * type of error that occurred. + * + * @param errorCode the error code for this rendering error. + */ + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + /** + * Returns the error code for this rendering error. + * + * @return the error code. + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Sets the error message for this rendering error. This is a short + * message describing the error, and is included as part of + * toString(). + * + * @param errorMessage a short error message describing this + * rendering error. + */ + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + /** + * Returns the error message for this rendering error. + * + * @return a short error message describing this rendering error. + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Sets the detail message for this rendering error. This is a more + * detailed error message that is not included as part of toString(). + * + * @param detailMessage a detailed message describing this + * error in more detail. + */ + public void setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + } + + /** + * Returns the detail message for this rendering error. + * + * @return the detail message for this rendering error. + */ + public String getDetailMessage() { + return detailMessage; + } + + /** + * Sets the graphics device associated with this rendering error. + * + * @param graphicsDevice the graphics device associated with this rendering error. + */ + public void setGraphicsDevice(GraphicsDevice graphicsDevice) { + this.graphicsDevice = graphicsDevice; + } + + /** + * Returns the graphics device associated with this rendering error. + * + * @return the graphics device associated with this rendering error. + */ + public GraphicsDevice getGraphicsDevice() { + return this.graphicsDevice; + } + + /** + * Sets the canvas associated with this rendering error. + * + * @param canvas the canvas associated with this rendering error. + */ + public void setCanvas3D(Canvas3D canvas) { + this.canvas = canvas; + } + + /** + * Returns the canvas associated with this rendering error. + * + * @return the canvas associated with this rendering error. + */ + public Canvas3D getCanvas3D() { + return this.canvas; + } + + + /** + * Returns a short string that describes this rendering error. The + * string is composed of the textual description of the errorCode, + * a ": ", and the errorMessage field. If the errorMessage is + * null then the ": " and the errorMessage are omitted. + * + * @return a string representation of this rendering error. + */ + @Override + public String toString() { + // Concatenate string representation of error code with error message + String errorCodeStr; + switch (errorCode) { + case NO_ERROR: + errorCodeStr = "NO_ERROR"; + break; + case UNEXPECTED_RENDERING_ERROR: + errorCodeStr = "UNEXPECTED_RENDERING_ERROR"; + break; + case GRAPHICS_CONFIG_ERROR: + errorCodeStr = "GRAPHICS_CONFIG_ERROR"; + break; + case CONTEXT_CREATION_ERROR: + errorCodeStr = "CONTEXT_CREATION_ERROR"; + break; + case OFF_SCREEN_BUFFER_ERROR: + errorCodeStr = "OFF_SCREEN_BUFFER_ERROR"; + break; + + default: + errorCodeStr = "UNKNOWN ERROR CODE (" + errorCode + ")"; + } + + if (errorMessage == null) { + return errorCodeStr; + } + + return errorCodeStr + ": " + errorMessage; + } +} diff --git a/src/javax/media/j3d/RenderingErrorListener.java b/src/javax/media/j3d/RenderingErrorListener.java new file mode 100644 index 0000000..3269e0d --- /dev/null +++ b/src/javax/media/j3d/RenderingErrorListener.java @@ -0,0 +1,43 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Listener interface for monitoring Java 3D rendering errors. + * + * @see VirtualUniverse#addRenderingErrorListener + * + * @since Java 3D 1.5 + */ +public interface RenderingErrorListener { + /** + * Invoked when an error occurs in the Java 3D rendering system. + * + * @param error object that contains the details of the error. + */ + public void errorOccurred(RenderingError error); +} diff --git a/src/javax/media/j3d/RestrictedAccessException.java b/src/javax/media/j3d/RestrictedAccessException.java new file mode 100644 index 0000000..3b992f5 --- /dev/null +++ b/src/javax/media/j3d/RestrictedAccessException.java @@ -0,0 +1,51 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/RotPosPathInterpolator.java b/src/javax/media/j3d/RotPosPathInterpolator.java new file mode 100644 index 0000000..4e91d69 --- /dev/null +++ b/src/javax/media/j3d/RotPosPathInterpolator.java @@ -0,0 +1,394 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Matrix4d; +import javax.vecmath.Point3f; +import javax.vecmath.Quat4f; +import javax.vecmath.Vector3f; + + +/** + * 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 = 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 + * TransformInterpolator.setTransformAxis(Transform3D) + */ + + public void setAxisOfRotPos(Transform3D axisOfRotPos) { + setTransformAxis(axisOfRotPos); + } + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.getTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + RotPosPathInterpolator rppi = new RotPosPathInterpolator(); + rppi.duplicateNode(this, forceDuplicate); + return rppi; + } + + + + /** + * Copies all RotPosPathInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/RotPosScalePathInterpolator.java b/src/javax/media/j3d/RotPosScalePathInterpolator.java new file mode 100644 index 0000000..3114f66 --- /dev/null +++ b/src/javax/media/j3d/RotPosScalePathInterpolator.java @@ -0,0 +1,466 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Matrix4d; +import javax.vecmath.Point3f; +import javax.vecmath.Quat4f; +import javax.vecmath.Vector3f; + + +/** + * 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 = 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 + * @param quat returns 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 + * @param position returns 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 + * TransformInterpolator.setTransformAxis(Transform3D) + */ + + public void setAxisOfRotPosScale(Transform3D axisOfRotPosScale) { + setTransformAxis(axisOfRotPosScale); + } + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.geTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + RotPosScalePathInterpolator ri = new RotPosScalePathInterpolator(); + ri.duplicateNode(this, forceDuplicate); + return ri; + } + + + /** + * Copies all RotPosScalePathInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/RotationInterpolator.java b/src/javax/media/j3d/RotationInterpolator.java new file mode 100644 index 0000000..f536685 --- /dev/null +++ b/src/javax/media/j3d/RotationInterpolator.java @@ -0,0 +1,218 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = 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 + * TransformInterpolator.setTransformAxis(Transform3D) + */ + public void setAxisOfRotation(Transform3D axisOfRotation) { + setTransformAxis(axisOfRotation); + } + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.getTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + RotationInterpolator ri = new RotationInterpolator(); + ri.duplicateNode(this, forceDuplicate); + return ri; + } + + + /** + * Copies all RotationInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + super.duplicateAttributes(originalNode, forceDuplicate); + + RotationInterpolator ri = (RotationInterpolator) originalNode; + + setMinimumAngle(ri.getMinimumAngle()); + setMaximumAngle(ri.getMaximumAngle()); + + } + + +} diff --git a/src/javax/media/j3d/RotationPathInterpolator.java b/src/javax/media/j3d/RotationPathInterpolator.java new file mode 100644 index 0000000..6f49627 --- /dev/null +++ b/src/javax/media/j3d/RotationPathInterpolator.java @@ -0,0 +1,312 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = 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 + * TransformInterpolator.seTransformAxis(Transform3D) + */ + public void setAxisOfRotation(Transform3D axisOfRotation) { + setTransformAxis(axisOfRotation); + } + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.getTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + RotationPathInterpolator rpi = new RotationPathInterpolator(); + rpi.duplicateNode(this, forceDuplicate); + return rpi; + } + + + /** + * Copies all RotationPathInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/ScaleInterpolator.java b/src/javax/media/j3d/ScaleInterpolator.java new file mode 100644 index 0000000..858967c --- /dev/null +++ b/src/javax/media/j3d/ScaleInterpolator.java @@ -0,0 +1,221 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = 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 + * TransformInterpolator.setTransformAxis(Transform3D) + */ + public void setAxisOfScale(Transform3D axisOfScale) { + setTransformAxis(axisOfScale); + } + + /** + * @deprecated As of Java 3D version 1.3, replaced by + * TransformInterpolator.getTransformAxis() + */ + 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + ScaleInterpolator si = new ScaleInterpolator(); + si.duplicateNode(this, forceDuplicate); + return si; + } + + + /** + * Copies all ScaleInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + + super.duplicateAttributes(originalNode, forceDuplicate); + + ScaleInterpolator si = (ScaleInterpolator) originalNode; + + setMinimumScale(si.getMinimumScale()); + setMaximumScale(si.getMaximumScale()); + + } +} diff --git a/src/javax/media/j3d/SceneGraphCycleException.java b/src/javax/media/j3d/SceneGraphCycleException.java new file mode 100644 index 0000000..baf8b9a --- /dev/null +++ b/src/javax/media/j3d/SceneGraphCycleException.java @@ -0,0 +1,58 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + *

    + *
  • is made live + *
  • is compiled + *
  • is cloned + *
  • has getBounds() called on it. + *
+ */ +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/javax/media/j3d/SceneGraphObject.java b/src/javax/media/j3d/SceneGraphObject.java new file mode 100644 index 0000000..db0eb53 --- /dev/null +++ b/src/javax/media/j3d/SceneGraphObject.java @@ -0,0 +1,513 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Hashtable; + +/** + * SceneGraphObject is the common superclass for all scene graph + * objects. Scene graph objects are classified into two main types: + * nodes and node components. The Node object is the common superclass + * of all nodes, which includes TransformGroup, Shape3D, etc. + * The NodeComponent object is the common superclass of all node + * components, which includes Geometry, Appearance, etc. + * + *

+ * All scene graph objects have a name, a user data object, a set of + * capability bits, and a set of capabilityIsFrequent bits. + * + *

+ * Capability bits control whether a particular attribute in a node or + * node component is readable or writable. For live or compiled scene + * graphs, only those attributes whose capabilities are set before the + * scene graph is compiled or made live may be read or written. The + * default value for all read capability bits is true, meaning + * that all attributes may be read by default. The default value for + * all write capability bits is false, meaning that no + * attributes may be written by default. Read capability bits are + * defined as those capability bits of the form ALLOW_*_READ, + * plus the ALLOW_INTERSECT capability bit. Write + * capability bits are defined as those capability bits of the form + * ALLOW_*_WRITE, plus the ALLOW_CHILDREN_EXTEND + * and ALLOW_DETACH capability bits. + * + *

+ * NOTE that the ENABLE_COLLISION_REPORTING and + * ENABLE_PICK_REPORTING bits are not really capability bits, + * although they are set with the setCapability method. The default value + * for each of the ENABLE_*_REPORTING bits is false. + * + *

+ * For more information, see the + * Introduction to the Java 3D API. + */ +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; + + // Optional name for object. + private String objectName = 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: + *

    + * all read capability bits : set (true)
    + * all write capability bits : clear (false)
    + * all capabilityIsFrequent bits : set (true)
    + * isLive : false
    + * isCompiled : false
    + * user data : null
    + * name : null
    + *
+ */ + 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 Retained(); + // this.retained.setSource(this); + } + + /** + * Method to set default read capability bits to true + */ + void setDefaultReadCapabilities(int[] bits) { + if (true /*VirtualUniverse.mc.defaultReadCapability*/) { + for (int i=0; i < bits.length; i++) { + setCapability(bits[i]); + } + } + } + + /** + * 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. + * + *

+ * Unlike setCapability, this method may be called on a live scene + * graph object (but not on a compiled object). + * + *

+ * 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. + * + *

+ * Unlike clearCapability, this method may be called on a live scene + * graph object (but not on a compiled object). + * + *

+ * 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 + * cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + public void updateNodeReferences(NodeReferenceTable referenceTable) { + } + + /** + * Sets the name of this object. Object names are for information + * only. + * + * @param name the new name of this object + * + * @since Java 3D 1.4 + */ + public void setName( String name ) { + objectName = name; + } + + /** + * Returns the name of this object. + * + * @return the name of this object + * + * @since Java 3D 1.4 + */ + public String getName() { + return objectName; + } + + /** + * Copies all SceneGraphObject information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * NOTE: Applications should not 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; + objectName = originalNode.objectName; + } + + + /** + * If forceDuplicate is true or + * duplicateOnCloneTree flag is true. This procedure + * will return a clone of originalNode or the value in + * in nodeHashtable if found. Otherwise return + * originalNode + * + * This method is called from the + * duplicateAttributes method during cloneNodeComponent. + * + * @param originalNodeComponent the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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; + } + } + + // Internal method to make a prefix out of the name of this object + String getNamePrefix() { + String name = getName(); + + if (name != null) { + return "[" + name + "] "; + } + + return ""; + } + + /** + * Returns a String representation of this SceneGraphObject. + * If its name is non-null, then it is concatenated with + * super.toString(). + */ + @Override + public String toString() { + return getNamePrefix() + super.toString(); + } + +} diff --git a/src/javax/media/j3d/SceneGraphObjectRetained.java b/src/javax/media/j3d/SceneGraphObjectRetained.java new file mode 100644 index 0000000..09000a7 --- /dev/null +++ b/src/javax/media/j3d/SceneGraphObjectRetained.java @@ -0,0 +1,184 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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.err.println(); + for (int i = 0; i < level; i++) { + System.err.print("."); + } + System.err.print(this); + } + + /** + * true if component can't be read or written after compile or setlive() + */ + boolean isStatic() { + return source.capabilityBitsEmpty(); + } + + @Override + protected Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + void handleFrequencyChange(int bit) { + } + + @Override + VirtualUniverse getVirtualUniverse() { + return null; + } + +} diff --git a/src/javax/media/j3d/SceneGraphPath.java b/src/javax/media/j3d/SceneGraphPath.java new file mode 100644 index 0000000..aac2f9d --- /dev/null +++ b/src/javax/media/j3d/SceneGraphPath.java @@ -0,0 +1,668 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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. + *

+ * 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: + *

    + * root : null
    + * object : null
    + * list of (interior) nodes : null
    + * transform : identity
    + *
+ */ + 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 + */ + @Override + 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 + */ + @Override + 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 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 + */ + @Override + 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 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=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.err.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/javax/media/j3d/Screen3D.java b/src/javax/media/j3d/Screen3D.java new file mode 100644 index 0000000..988dc97 --- /dev/null +++ b/src/javax/media/j3d/Screen3D.java @@ -0,0 +1,468 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.Dimension; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Rectangle; +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. + *

+ * Default values for Screen3D parameters are as follows: + *

    + * physical screen width : 0.0254/90.0 * screen width (in pixels)
    + * physical screen height : 0.0254/90.0 * screen height (in pixels)
    + * tracker base to image plate transform : identity
    + * head tracker to left image plate transform : identity
    + * head tracker to right image plate transform : identity
    + * off-screen size : (0,0)
    + *
+ *

+ * Offscreen Rendering

+ * 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.

+ * Calibration Parameters

+ * The Screen3D object must be calibrated with the coexistence volume. + * The Screen3D class provides several methods for defining the + * calibration parameters.

+ *

    Measured Parameters

    + * 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.

    + * + * Head-tracker Coordinate System

    + * If head tracking is enabled, one of two parameters need to be specified:

    + *

    • 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.
    • + * + *

    • 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).
    + *

+ * + *

+ * Additional Information + *

+ * For more information, see the + * Introduction to the Java 3D API and + * View Model + * documents. + * + * @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; + + // Screen number + 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 list of Canvas3D Objects that refer to this + private final ArrayList users = new ArrayList(); + +// Add a user to the list of users +synchronized void removeUser(Canvas3D c) { + users.remove(c); +} + +// Add a user to the list of users +synchronized void addUser(Canvas3D c) { + if (!users.contains(c)) + users.add(c); +} + +// Add a user to the list of users +synchronized void notifyUsers() { + for (int i = 0; i < users.size(); i++) { + users.get(i).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 rv is null, a new Dimension object is allocated. + * + * @return rv + * + * @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). + *
+ * 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). + *
+ * 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; + } + + @Override + 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) { + this.offScreen = offScreen; + this.graphicsDevice = graphicsConfiguration.getDevice(); + + screenViewCache = new ScreenViewCache(this); + + // Get the display handle and the screen number from the Pipeline + screen = Pipeline.getPipeline().getScreen(graphicsDevice); + + if (debug) + System.err.println("Screen3D: 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() { + synchronized(this) { + screenViewCache.snapshot(); + } + } +} diff --git a/src/javax/media/j3d/ScreenViewCache.java b/src/javax/media/j3d/ScreenViewCache.java new file mode 100644 index 0000000..bc4cdd1 --- /dev/null +++ b/src/javax/media/j3d/ScreenViewCache.java @@ -0,0 +1,134 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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. + // Issue 163: Array of dirty bits is used because the Renderer and + // RenderBin run asynchronously. Now that they each have a separate + // instance of CanvasViewCache (due to the fix for Issue 109), they + // need separate dirty bits. Array element 0 is used for the Renderer and + // element 1 is used for the RenderBin. + int[] scrvcDirtyMask = new int[2]; + + // + // 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[0] |= screen.scrDirtyMask; + scrvcDirtyMask[1] |= screen.scrDirtyMask; + } else { + scrvcDirtyMask[0] = screen.scrDirtyMask; + scrvcDirtyMask[1] = 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.err.println("Constructed a ScreenViewCache"); + } +} diff --git a/src/javax/media/j3d/Sensor.java b/src/javax/media/j3d/Sensor.java new file mode 100644 index 0000000..dea9fbf --- /dev/null +++ b/src/javax/media/j3d/Sensor.java @@ -0,0 +1,544 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Matrix3d; +import javax.vecmath.Point3d; + +/** + * 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.

+ * + * 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).

+ * + * 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.

+ * + * 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.

+ * + * 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.

+ * + * 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.

+ * + * @see SensorRead + */ + +public class Sensor { + + /** + * Set predictor type to do no prediction; this is the default. + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public static final int PREDICT_NONE = 1; + + /** + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public static final int PREDICT_NEXT_FRAME_TIME = 2; + + /** + * Use no prediction policy; this is the default. + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public static final int NO_PREDICTOR = 16; + + /** + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public static final int HEAD_PREDICTOR = 32; + + /** + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + 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; + + // Prediction policy -- unused + private int predictionPolicy = NO_PREDICTOR; + + // Predictor type -- unused + private 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(); + + + /** + * Constructs a Sensor object for the specified input device using + * default parameters. The default values are as follows: + *

    + * sensor read count : 30
    + * sensor button count : 0
    + * hot spot : (0,0,0)
    + * predictor : PREDICT_NONE — this attribute is unused
    + * prediction policy : NO_PREDICTOR — this attribute is unused
    + *
+ * @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); + } + + // 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); + } + + /** + * Sets the type of predictor to use with this sensor. + * Since prediction is not implemented (and never has been), this + * attribute has no effect. + * @param predictor predictor type one of PREDICT_NONE or + * PREDICT_NEXT_FRAME_TIME + * @exception IllegalArgumentException if an invalid predictor type + * is specified. + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public void setPredictor(int predictor){ + if (predictor != PREDICT_NONE && predictor != PREDICT_NEXT_FRAME_TIME) { + throw new IllegalArgumentException(J3dI18N.getString("Sensor0")); + } else { + predictorType = predictor; + } + } + + /** + * Returns the type of predictor used by this sensor. + * @return the predictor type. + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public int getPredictor(){ + return predictorType; + } + + /** + * Sets the prediction policy use by this sensor. + * Since prediction is not implemented (and never has been), this + * attribute has no effect. + * @param policy prediction policy one of NO_PREDICTOR, HEAD_PREDICTOR, + * or HAND_PREDICTOR + * @exception IllegalArgumentException if an invalid prediction policy + * is specified. + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + public void setPredictionPolicy(int policy){ + if (policy != NO_PREDICTOR && policy != HEAD_PREDICTOR && + policy != HAND_PREDICTOR) + throw new IllegalArgumentException(J3dI18N.getString("Sensor1")); + else + predictionPolicy = policy; + } + + /** + * Returns the prediction policy used by this sensor. + * @return the prediction policy. + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature. + */ + 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; + } + + /** + * Retrieves the last sensor reading and copies that value into + * the specified argument. + * + * @param read the matrix that will receive the sensor reading + */ + public void getRead(Transform3D read) { + if(demand_driven == true) + device.pollAndProcessInput(); + + read.set(readings[currentIndex].read); + } + + /** + * Retrieves the last sensor reading and copies that value into + * the specified argument. + * + * @param read the matrix that will receive the sensor reading + * @param deltaT this parameter is ignored + * + * @deprecated As of Java 3D version 1.4, prediction is not a + * supported feature; use getRead(Transform3D) instead. + */ + public void getRead(Transform3D read, long deltaT){ + getRead(read); + } + + /** + * 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; + } + +} diff --git a/src/javax/media/j3d/SensorRead.java b/src/javax/media/j3d/SensorRead.java new file mode 100644 index 0000000..e64c9c5 --- /dev/null +++ b/src/javax/media/j3d/SensorRead.java @@ -0,0 +1,180 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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: + *
    + * number of buttons : 0
    + * button values : 0 (for all array elements)
    + * transform : identity
    + * time : current time
    + *
+ */ + 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 = J3dClock.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/javax/media/j3d/SetLiveState.java b/src/javax/media/j3d/SetLiveState.java new file mode 100644 index 0000000..42ee62a --- /dev/null +++ b/src/javax/media/j3d/SetLiveState.java @@ -0,0 +1,270 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + // XXXX: optimization for targetThreads computation, require + // cleanup in GroupRetained.doSetLive() + //transformTargetThreads = 0; + + hashkeyIndex = null; + + // Fix for issue 75 + parentBranchGroupPaths = null; + branchGroupPaths = null; + orderedPaths = null; + } +} diff --git a/src/javax/media/j3d/Shader.java b/src/javax/media/j3d/Shader.java new file mode 100644 index 0000000..8aa700c --- /dev/null +++ b/src/javax/media/j3d/Shader.java @@ -0,0 +1,145 @@ +/* + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The Shader object is the abstract base class for programmable + * shader code. Currently, only text-based source code shaders are + * supported, so the only subclass of Shader is SourceCodeShader. We + * leave open the possibility for binary (object code) shaders in the + * future. + * + *

+ * Each instance of a Shader object allows an application to specify + * the source code used in programming the Graphics Pipeline Unit + * (GPU) of the graphics accelerator. A Shader object is constructed + * with modes that specify the shading language and the + * shader type. + * + *

+ * The shading language specifies the language and runtime environment + * used to program the GPU. The currently defined shading languages + * are GLSL (also known as the OpenGL 2.0 shading language) and + * Cg. Note that not all shading languages are supported on all + * platforms. It is up to the application or utility to query whether + * a particular shading language is supported before using it. The + * value of the shadingLanguage mode is one of: + * SHADING_LANGUAGE_GLSL or + * SHADING_LANGUAGE_CG. + * + *

+ * The shader type specifies whether the shader is a vertex + * shader or a fragment shader. A vertex shader replaces + * the fixed-function graphics pipeline for vertex operations + * (transformation and lighting). A fragment shader replaces the + * fixed-function graphics pipeline for fragment shading operations + * (texture mapping, texture application, coloring, shading, and so + * forth). The value of the shaderType mode is one of: + * SHADER_TYPE_VERTEX or + * SHADER_TYPE_FRAGMENT. + * + *

+ * Both the shading language and shader type are immutable modes of + * the Shader object. + * + *

+ * NOTE: Applications should not extend this class. + * + * @see ShaderProgram + * @see Canvas3D#isShadingLanguageSupported + * + * @since Java 3D 1.4 + */ + +public abstract class Shader extends NodeComponent { + + + /** + * This constant indicates the GLSL shading language. It is one + * of the possible values of the shadingLanguage parameter. + */ + public static final int SHADING_LANGUAGE_GLSL = 1; + + /** + * This constant indicates the Cg shading language. It is one + * of the possible values of the shadingLanguage parameter. + */ + public static final int SHADING_LANGUAGE_CG = 2; + + + /** + * This constant indicates that the shader type is a vertex + * shader. It is one of the possible values of the shaderType + * parameter. + */ + public static final int SHADER_TYPE_VERTEX = 1; + + /** + * This constant indicates that the shader type is a fragment + * shader. It is one of the possible values of the shaderType + * parameter. + */ + public static final int SHADER_TYPE_FRAGMENT = 2; + + + /** + * Not a public constructor, for internal use + */ + Shader() { + } + + /** + * Package scope constructor so it can't be subclassed by classes + * outside the javax.media.j3d package. + */ + Shader(int shadingLanguage, int shaderType) { + ((ShaderRetained)this.retained).initializeShader(shadingLanguage, shaderType); + } + + /** + * Returns the shading language of this shader. + * + * @return the shading language of this shader, one of: + * SHADING_LANGUAGE_GLSL or + * SHADING_LANGUAGE_CG. + */ + public int getShadingLanguage() { + return ((ShaderRetained)this.retained).getShadingLanguage(); + } + + /** + * Returns the type of this shader. + * + * @return the shader type, one of: + * SHADER_TYPE_VERTEX or + * SHADER_TYPE_FRAGMENT. + */ + public int getShaderType() { + return ((ShaderRetained)this.retained).getShaderType(); + } +} + diff --git a/src/javax/media/j3d/ShaderAppearance.java b/src/javax/media/j3d/ShaderAppearance.java new file mode 100644 index 0000000..62da973 --- /dev/null +++ b/src/javax/media/j3d/ShaderAppearance.java @@ -0,0 +1,304 @@ +/* + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Hashtable; + +/** + *

The ShaderAppearance object defines programmable shading attributes + * that can be set as a component object of a Shape3D node. The + * ShaderAppearance rendering state adds the following attributes in + * addition to those defined by Appearance:

+ * + *
    + *
  • Shader program - specifies the shader program...
  • + * + *

    + *
  • Shader attribute set - specifies the shader parameters, both as + * explicit attributes and as implicit bindings to Java 3D + * state...
  • + *
+ * + *

The ShaderAppearance object modifies the definition of some of the + * attributes in Appearance:

+ * + *
    + *
  • Coloring attributes - XXXXX
  • + * + *

    + *
  • Line attributes - XXXXX
  • + * + *

    + *
  • Point attributes - XXXXX
  • + * + *

    + *
  • Polygon attributes - XXXXX
  • + * + *

    + *
  • Rendering attributes - XXXXX
  • + * + *

    + *
  • Transparency attributes - XXXXX
  • + * + *

    + *
  • Material - XXXXX
  • + * + *

    + *
  • Texture - XXXXX
  • + * + *

    + *
  • Texture attributes - XXXXX
  • + * + *

    + *
  • Texture coordinate generation - XXXXX
  • + * + *

    + *
  • Texture unit state - XXXXX
  • + *
+ * + * @see ShaderProgram + * @see ShaderAttributeSet + * + * @since Java 3D 1.4 + */ +public class ShaderAppearance extends Appearance { + /** + * Specifies that this ShaderAppearance object allows reading its + * ShaderProgram component information. + */ + public static final int + ALLOW_SHADER_PROGRAM_READ = + CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_READ; + + /** + * Specifies that this ShaderAppearance object allows writing its + * ShaderProgram component information. + */ + public static final int + ALLOW_SHADER_PROGRAM_WRITE = + CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_WRITE; + + /** + * Specifies that this ShaderAppearance object allows reading its + * ShaderAttributeSet component information. + */ + public static final int + ALLOW_SHADER_ATTRIBUTE_SET_READ = + CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_READ; + + /** + * Specifies that this ShaderAppearance object allows writing its + * ShaderAttributeSet component information. + */ + public static final int + ALLOW_SHADER_ATTRIBUTE_SET_WRITE = + CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_WRITE; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SHADER_PROGRAM_READ, + ALLOW_SHADER_ATTRIBUTE_SET_READ + }; + + /** + * Constructs a ShaderAppearance component object using defaults for all + * state variables. All component object references are initialized + * to null. + */ + public ShaderAppearance() { + // Just use default values + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Creates the retained mode ShaderAppearanceRetained object that this + * ShaderAppearance component object will point to. + */ + @Override + void createRetained() { + this.retained = new ShaderAppearanceRetained(); + this.retained.setSource(this); + } + + /** + * Sets the ShaderProgram object to the specified object. Setting it to + * null causes a default pass-through shader to be used ??? + * + * @param shaderProgram object that specifies the desired shader program + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void setShaderProgram(ShaderProgram shaderProgram) { + + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_SHADER_PROGRAM_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance0")); + } + + ((ShaderAppearanceRetained)this.retained).setShaderProgram(shaderProgram); + + } + + + /** + * Retrieves the current ShaderProgram object. + * + * @return the ShaderProgram object + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public ShaderProgram getShaderProgram() { + + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_SHADER_PROGRAM_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance1")); + } + return ((ShaderAppearanceRetained)this.retained).getShaderProgram(); + } + + + /** + * Sets the ShaderAttributeSet object to the specified object. Setting it to + * null is equivalent to specifying an empty set of attributes. + * + * @param shaderAttributeSet object that specifies the desired shader attributes + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void setShaderAttributeSet(ShaderAttributeSet shaderAttributeSet) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_SHADER_ATTRIBUTE_SET_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance2")); + } + + ((ShaderAppearanceRetained)this.retained).setShaderAttributeSet(shaderAttributeSet); + } + + + /** + * Retrieves the current ShaderAttributeSet object. + * + * @return the ShaderAttributeSet object + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public ShaderAttributeSet getShaderAttributeSet() { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_SHADER_ATTRIBUTE_SET_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance3")); + } + return ((ShaderAppearanceRetained)this.retained).getShaderAttributeSet(); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + ShaderAppearance a = new ShaderAppearance(); + a.duplicateNodeComponent(this); + return a; + } + + /** + * 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) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } + + /** + * Copies all ShaderAppearance information from + * originalNodeComponent into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNodeComponent the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(NodeComponent originalNodeComponent, + boolean forceDuplicate) { + super.duplicateAttributes(originalNodeComponent, forceDuplicate); + + Hashtable hashtable = originalNodeComponent.nodeHashtable; + + ShaderAppearanceRetained app = + (ShaderAppearanceRetained) originalNodeComponent.retained; + + ShaderAppearanceRetained rt = (ShaderAppearanceRetained) retained; + + rt.setShaderProgram((ShaderProgram) getNodeComponent(app.getShaderProgram(), + 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. + */ + @Override + boolean duplicateChild() { + if (super.duplicateChild()) + return true; + + if (getDuplicateOnCloneTree()) + return true; + + ShaderAppearanceRetained rt = (ShaderAppearanceRetained) retained; + + NodeComponent nc; + + nc = rt.getShaderProgram(); + if ((nc != null) && nc.getDuplicateOnCloneTree()) + return true; + + return false; + } + +} diff --git a/src/javax/media/j3d/ShaderAppearanceRetained.java b/src/javax/media/j3d/ShaderAppearanceRetained.java new file mode 100644 index 0000000..da41886 --- /dev/null +++ b/src/javax/media/j3d/ShaderAppearanceRetained.java @@ -0,0 +1,397 @@ +/* + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + + +/** + * The Appearance object defines all rendering state that can be set + * as a component object of a Shape3D node. + */ +class ShaderAppearanceRetained extends AppearanceRetained { + + // Issue 485 - these values must start after the last value in Appearance + static final int SHADER_PROGRAM = 0x0800; + static final int SHADER_ATTRIBUTE_SET = 0x1000; + + // + // State variables: these should all be initialized to approproate + // Java 3D defaults. + // + + protected ShaderProgramRetained shaderProgram = null; + protected ShaderAttributeSetRetained shaderAttributeSet = null; + protected boolean isMirror = false; // For Debugging. + + /** + * Set the shader program object to the specified object. + * @param shaderProgram object that specifies the desired shader program + * and shader program attributes. + */ + void setShaderProgram(ShaderProgram sp) { + synchronized(liveStateLock) { + if (source.isLive()) { + // System.err.println("**** ShaderAppearceRetained.setShaderProgram()"); + + if (this.shaderProgram != null) { + this.shaderProgram.clearLive(refCount); + this.shaderProgram.removeMirrorUsers(this); + } + + if (sp != null) { + ((ShaderProgramRetained)sp.retained).setLive(inBackgroundGroup, + refCount); + ((ShaderProgramRetained)sp.retained).copyMirrorUsers(this); + } + + sendMessage(SHADER_PROGRAM, + (sp != null ? ((ShaderProgramRetained)sp.retained).mirror : null)); + + } + + if (sp == null) { + this.shaderProgram = null; + } else { + this.shaderProgram = (ShaderProgramRetained)sp.retained; + } + } + } + + + /** + * Retrieves the current shader program object. + * @return current shader program object + */ + ShaderProgram getShaderProgram() { + return (shaderProgram == null ? null : (ShaderProgram)shaderProgram.source); + } + + + /** + * Sets the ShaderAttributeSet object to the specified object. Setting it to + * null is equivalent to specifying an empty set of attributes. + * + * @param shaderAttributeSet object that specifies the desired shader attributes + */ + void setShaderAttributeSet(ShaderAttributeSet sas) { + synchronized(liveStateLock) { + if (source.isLive()) { + // System.err.println("**** ShaderAppearceRetained.setShaderAttributeSet()"); + + if (this.shaderAttributeSet != null) { + this.shaderAttributeSet.clearLive(refCount); + this.shaderAttributeSet.removeMirrorUsers(this); + } + + if (sas != null) { + ((ShaderAttributeSetRetained)sas.retained).setLive(inBackgroundGroup, + refCount); + ((ShaderAttributeSetRetained)sas.retained).copyMirrorUsers(this); + } + + // System.err.println(" -- testing needed!"); + sendMessage(SHADER_ATTRIBUTE_SET, + (sas != null ? + ((ShaderAttributeSetRetained)sas.retained).mirror : null)); + + } + + if (sas == null) { + this.shaderAttributeSet = null; + } else { + this.shaderAttributeSet = (ShaderAttributeSetRetained)sas.retained; + } + } + } + + + /** + * Retrieves the current ShaderAttributeSet object. + * @return current ShaderAttributeSet object + */ + ShaderAttributeSet getShaderAttributeSet() { + return (shaderAttributeSet == null ? null : (ShaderAttributeSet)shaderAttributeSet.source); + + } + + + @Override + public boolean equals(Object obj) { + return ((obj instanceof ShaderAppearanceRetained) && + equals((ShaderAppearanceRetained) obj)); + } + + boolean equals(ShaderAppearanceRetained sApp) { + boolean flag; + flag = (sApp == this); + + // If the reference is the same, we can stop check. + if(flag) + return flag; + + // Check each member's reference for equal. + flag = ((sApp != null) && + (shaderProgram == sApp.shaderProgram) && + (shaderAttributeSet == sApp.shaderAttributeSet)); + + + if (!flag) + return flag; + + return super.equals(sApp); + + } + + + + @Override + synchronized void createMirrorObject() { + // System.err.println("ShaderAppearanceRetained : 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 ShaderAppearanceRetained(); + ((ShaderAppearanceRetained)mirror).isMirror = true; // For Debugging. + } + 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. + */ + @Override + synchronized void initMirrorObject() { + // System.err.println("ShaderAppearanceRetained : initMirrorObject()"); + + super.initMirrorObject(); + + ShaderAppearanceRetained mirrorApp = (ShaderAppearanceRetained)mirror; + + if(shaderProgram != null) { + mirrorApp.shaderProgram = (ShaderProgramRetained)shaderProgram.mirror; + } + else { + mirrorApp.shaderProgram = null; + } + + if(shaderAttributeSet != null) { + mirrorApp.shaderAttributeSet = + (ShaderAttributeSetRetained)shaderAttributeSet.mirror; + } + else { + // System.err.println("shaderAttributeSet is null"); + mirrorApp.shaderAttributeSet = null; + } + + } + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + +// System.err.println("ShaderAppearanceRetained : updateMirrorObject(): " + +// "this = " + this + " component = " + component + " value = " + value); + super.updateMirrorObject(component, value); + ShaderAppearanceRetained mirrorApp = (ShaderAppearanceRetained)mirror; + if ((component & SHADER_PROGRAM) != 0) { + mirrorApp.shaderProgram = (ShaderProgramRetained)value; + } + else if ((component & SHADER_ATTRIBUTE_SET) != 0) { + mirrorApp.shaderAttributeSet = (ShaderAttributeSetRetained)value; + } + + } + + /** + * This method calls the setLive method of all appearance bundle + * objects. + */ + @Override + void doSetLive(boolean backgroundGroup, int refCount) { + // System.err.println("ShaderAppearceRetained.doSetLive()"); + + + if (shaderProgram != null) { + shaderProgram.setLive(backgroundGroup, refCount); + } + + if (shaderAttributeSet != null) { + shaderAttributeSet.setLive(backgroundGroup, refCount); + } + + + // Increment the reference count and initialize the appearance + // mirror object + super.doSetLive(backgroundGroup, refCount); + } + + + /** + * This clearLive routine first calls the superclass's method, then + * it removes itself to the list of lights + */ + @Override + void clearLive(int refCount) { + super.clearLive(refCount); + + if (shaderProgram != null) { + shaderProgram.clearLive(refCount); + } + + if (shaderAttributeSet != null) { + shaderAttributeSet.clearLive(refCount); + } + } + + @Override + synchronized void addAMirrorUser(Shape3DRetained shape) { + + super.addAMirrorUser(shape); + if (shaderProgram != null) + shaderProgram.addAMirrorUser(shape); + if (shaderAttributeSet != null) + shaderAttributeSet.addAMirrorUser(shape); + } + + @Override + synchronized void removeAMirrorUser(Shape3DRetained shape) { + + super.removeAMirrorUser(shape); + if (shaderProgram != null) + shaderProgram.removeAMirrorUser(shape); + if (shaderAttributeSet != null) + shaderAttributeSet.removeAMirrorUser(shape); + } + + + 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 = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES; + createMessage.type = J3dMessage.SHADER_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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + } + + + @Override + boolean isStatic() { + if (!super.isStatic()) { + return false; + } + + boolean flag = + source.capabilityBitsEmpty() && + ((shaderProgram == null) || + shaderProgram.source.capabilityBitsEmpty()) && + ((shaderAttributeSet == null) || + shaderAttributeSet.source.capabilityBitsEmpty()); + + return flag; + } + + // Issue 209 - implement the compile method + // Simply pass along to the NodeComponents + @Override + void compile(CompileState compState) { + super.compile(compState); + + if (shaderProgram != null) { + shaderProgram.compile(compState); + } + + if (shaderAttributeSet != null) { + shaderAttributeSet.compile(compState); + } + } + + @Override + boolean isOpaque(int geoType) { + + if (!super.isOpaque(geoType)) { + return false; + } + + // TODO: IMPLEMENT THIS + // TODO: How do we determine whether a ShaderAppearance is opaque??? + return true; + } + + @Override + void handleFrequencyChange(int bit) { + // System.err.println("ShaderAppearanceRetained : handleFrequencyChange()"); + super.handleFrequencyChange(bit); + + int mask = 0; + if (bit == ShaderAppearance.ALLOW_SHADER_PROGRAM_WRITE) + mask = SHADER_PROGRAM; + else if (bit == ShaderAppearance.ALLOW_SHADER_ATTRIBUTE_SET_WRITE) + mask = SHADER_ATTRIBUTE_SET; + + + if (mask != 0) + setFrequencyChangeMask(bit, mask); + } +} + + diff --git a/src/javax/media/j3d/ShaderAttrLoc.java b/src/javax/media/j3d/ShaderAttrLoc.java new file mode 100644 index 0000000..5e36adc --- /dev/null +++ b/src/javax/media/j3d/ShaderAttrLoc.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Tagging interface for shader attribute location objects. The rendering + * pipelines will define concrete classes that implement this interface. All + * code that uses the tagged objects will be in the pipelines. + */ +interface ShaderAttrLoc { + // No methods or constants defined at this time +} diff --git a/src/javax/media/j3d/ShaderAttribute.java b/src/javax/media/j3d/ShaderAttribute.java new file mode 100644 index 0000000..2dae7fe --- /dev/null +++ b/src/javax/media/j3d/ShaderAttribute.java @@ -0,0 +1,90 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttribute object encapsulates a uniform attribute for a + * shader programs. Uniform attributes (variables) are those + * attributes whose values are constant during the rendering of a + * primitive. Their values may change from primitive to primitive, but + * are constant for each vertex (for vertex shaders) or fragment (for + * fragment shaders) of a single primitive. Examples of uniform + * attributes include a transformation matrix, a texture map, lights, + * lookup tables, etc. + * + *

+ * There are two ways in which values can be specified for uniform + * attributes: explicitly, by providing a value; and implicitly, by + * defining a binding between a Java 3D system attribute and a uniform + * attribute. This functionality is provided by two subclasses of + * ShaderAttribute as follows: + * + *

    + *
  • ShaderAttributeObject, in which attributes are expressed as + * (attrName, value) pairs, is used for explicitly + * defined attributes
  • + *
  • ShaderAttributeBinding, in which attributes are expressed as + * (attrName, j3dAttrName) pairs, is used for + * implicitly defined, automatically tracked attributes
  • + *
+ * + * @see ShaderAttributeSet + * @see ShaderProgram + * + * @since Java 3D 1.4 + */ + +public abstract class ShaderAttribute extends NodeComponent { + /** + * Name of the shader attribute (immutable) + */ + + /** + * Package scope constructor + * + */ + ShaderAttribute(String attrName) { + if (attrName == null) { + throw new NullPointerException(); + } + + ((ShaderAttributeRetained)this.retained).initializeAttrName(attrName); + } + + /** + * Retrieves the name of this shader attribute. + * + * @return the name of this shader attribute + */ + public String getAttributeName() { + + return ((ShaderAttributeRetained)this.retained).getAttributeName(); + + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeArray.java b/src/javax/media/j3d/ShaderAttributeArray.java new file mode 100644 index 0000000..13be68a --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeArray.java @@ -0,0 +1,163 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeArray object encapsulates a uniform shader + * attribute whose value is specified explicitly. The shader variable + * attrName is explicitly set to the specified + * value during rendering. attrName must be + * the name of a valid uniform attribute in the shader in which it is + * used. Otherwise, the attribute name will be ignored and a runtime + * error may be generated. The value must be an array + * of one of the allowed classes. The allowed classes are: + * Integer[], Float[], + * Tuple{2,3,4}{i,f}[], Matrix{3,4}f[]. A + * ClassCastException will be thrown if a specified value + * object is not one of the allowed types. Further, the type and length of the + * value is immutable once a ShaderAttributeArray is constructed. + * Subsequent setValue operations must be called with an array of the + * same type and length as the one that was used to construct the + * ShaderAttributeArray. Finally, the type of the value + * object must match the type of the corresponding + * attrName variable in the shader in which it is + * used. Otherwise, the shader will not be able to use the attribute + * and a runtime error may be generated. + * + * @see ShaderAttributeSet + * @see ShaderProgram + * + * @since Java 3D 1.4 + */ + +public class ShaderAttributeArray extends ShaderAttributeObject { + /** + * Constructs a new ShaderAttributeArray object with the specified + * (attrName, value) pair. The specified value + * must be an array of one of the allowed class types. + * A deep copy of the array is stored. + * + * @param attrName the name of the shader attribute + * @param value the value of the shader attribute + * + * @exception NullPointerException if attrName or value is null + * + * @exception ClassCastException if value is not an array of + * one of the allowed classes + */ + public ShaderAttributeArray(String attrName, Object value) { + super(attrName, value); + } + + // Implement abstract getValue method + @Override + public Object getValue() { + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_VALUE_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject0")); + + return ((ShaderAttributeArrayRetained)this.retained).getValue(); + } + + // Implement abstract setValue method + @Override + public void setValue(Object value) { + if (value == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_VALUE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject1")); + + if (isLive()) + ((ShaderAttributeArrayRetained)this.retained).setValue(value); + else + ((ShaderAttributeArrayRetained)this.retained).initValue(value); + + } + + + /** + * Sets the specified array element of the value of this shader + * attribute to the specified value. + * A copy of the object is stored. + * + * @param value the new value of the shader attribute + * + * @exception NullPointerException if value is null + * + * @exception ClassCastException if value is not an instance of + * the same base class as the individual elements of the array object + * used to construct this shader attribute object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void setValue(int index, Object value) { + if (value == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_VALUE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject1")); + + if (isLive()) + ((ShaderAttributeArrayRetained)this.retained).setValue(index, value); + else { + ((ShaderAttributeArrayRetained)this.retained).initValue(index, value); + } + } + + /** + * Returns the number of elements in the value array. + * + * @return the number of elements in the value array + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public int length() { + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_VALUE_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject0")); + + return ((ShaderAttributeArrayRetained)this.retained).length(); + } + + /** + * Creates a retained mode ShaderAttributeArrayRetained object that this + * ShaderAttributeArray component object will point to. + */ + @Override + void createRetained() { + this.retained = new ShaderAttributeArrayRetained(); + this.retained.setSource(this); + } +} diff --git a/src/javax/media/j3d/ShaderAttributeArrayRetained.java b/src/javax/media/j3d/ShaderAttributeArrayRetained.java new file mode 100644 index 0000000..48111bf --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeArrayRetained.java @@ -0,0 +1,1057 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4f; +import javax.vecmath.Point2f; +import javax.vecmath.Point2i; +import javax.vecmath.Point3f; +import javax.vecmath.Point3i; +import javax.vecmath.Point4f; +import javax.vecmath.Point4i; +import javax.vecmath.Tuple2f; +import javax.vecmath.Tuple2i; +import javax.vecmath.Tuple3f; +import javax.vecmath.Tuple3i; +import javax.vecmath.Tuple4f; +import javax.vecmath.Tuple4i; + +/** + * The ShaderAttributeArray object encapsulates a uniform shader + * attribute whose value is specified explicitly. + */ + +class ShaderAttributeArrayRetained extends ShaderAttributeObjectRetained { + + ShaderAttributeArrayRetained() { + } + + void initValue(int index, Object value) { + /* + System.err.println("ShaderAttributeObjectRetained : attrName = " + attrName + + ", index = " + index + ", value = " + value + + ", value.class = " + value.getClass()); + */ + ((ArrayWrapper)attrWrapper).set(index, value); + + } + + + /** + * Sets the specified array element of the value of this shader + * attribute to the specified value. + * A copy of the object is stored. + * + * @param value the new value of the shader attribute + * + * @exception NullPointerException if value is null + * + * @exception ClassCastException if value is not an instance of + * the same base class as the individual elements of the array object + * used to construct this shader attribute object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + void setValue(int index, Object value) { + initValue(index, value); + // We should only need to update the array instead of replacing it. + // Until this become a really bottleneck, it will just be a convenience + // method for end user. + // An efficient approach is to + // (1) Create a new ShaderAttributeValue object for the "value" object + // and pass it to sendMessage(), (2) Create a new sendMessage that take in + // a third arguement, ie. index. + setValue(attrWrapper.getRef()); + } + + /** + * Returns the number of elements in the value array. + * + * @return the number of elements in the value array + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + int length() { + return ((ArrayWrapper)attrWrapper).length(); + + } + + // Helper methods ... + + @Override + synchronized void createMirrorObject() { + // System.err.println("ShaderAttributeArrayRetained : createMirrorObject"); + // This method should only call by setLive(). + if (mirror == null) { + ShaderAttributeArrayRetained mirrorSAA = new ShaderAttributeArrayRetained(); + mirrorSAA.createObjectData(getValue()); + mirror = mirrorSAA; + mirror.source = source; + + } + initMirrorObject(); + } + + + /** + * Computes the base class from the specified object. A + * ClassCastException is thrown if the object is not an array of + * one of the allowed classes. + */ + @Override + int computeClassType(Object value) { + Class objClass = value.getClass(); + if (!objClass.isArray()) { + throw new ClassCastException(objClass + " -- must be array class"); + } + + for (int i = 0; i < classTable.length; i++) { + if (classTableArr[i].isInstance(value)) { + return i; + } + } + throw new ClassCastException(objClass + " -- unrecognized class"); + } + + /** + * Returns the base class represented by the specified class type. + */ + @Override + Class getBaseClass(int classType) { + return classTableArr[classType]; + } + + /** + * Creates an attribute wrapper object of the specified class + * type, and stores the specified array of objects. + */ + @Override + AttrWrapper createAttrWrapper(Object value, int classType) { + ArrayWrapper attrWrapper = null; + switch (classType) { + case TYPE_INTEGER: + attrWrapper = new IntegerArrayWrapper(); + break; + case TYPE_FLOAT: + attrWrapper = new FloatArrayWrapper(); + break; +// case TYPE_DOUBLE: +// attrWrapper = new DoubleArrayWrapper(); +// break; + case TYPE_TUPLE2I: + attrWrapper = new Tuple2iArrayWrapper(); + break; + case TYPE_TUPLE2F: + attrWrapper = new Tuple2fArrayWrapper(); + break; +// case TYPE_TUPLE2D: +// attrWrapper = new Tuple2dArrayWrapper(); +// break; + case TYPE_TUPLE3I: + attrWrapper = new Tuple3iArrayWrapper(); + break; + case TYPE_TUPLE3F: + attrWrapper = new Tuple3fArrayWrapper(); + break; +// case TYPE_TUPLE3D: +// attrWrapper = new Tuple3dArrayWrapper(); +// break; + case TYPE_TUPLE4I: + attrWrapper = new Tuple4iArrayWrapper(); + break; + case TYPE_TUPLE4F: + attrWrapper = new Tuple4fArrayWrapper(); + break; +// case TYPE_TUPLE4D: +// attrWrapper = new Tuple4dArrayWrapper(); +// break; + case TYPE_MATRIX3F: + attrWrapper = new Matrix3fArrayWrapper(); + break; +// case TYPE_MATRIX3D: +// attrWrapper = new Matrix3dArrayWrapper(); +// break; + case TYPE_MATRIX4F: + attrWrapper = new Matrix4fArrayWrapper(); + break; +// case TYPE_MATRIX4D: +// attrWrapper = new Matrix4dArrayWrapper(); +// break; + default: + // Should never get here + assert false; + return null; + } + + attrWrapper.set(value); + return attrWrapper; + } + + + // + // The following wrapper classes are used to store a copy of the + // user-specified shader attribute value. There is a wrapper class + // for each supported base class. + // + + // Base wrapper class for array attribute types + static abstract class ArrayWrapper extends AttrWrapper { + int length = 0; + + /** + * Returns the length of the array + */ + int length() { + return length; + } + + /** + * Sets the specified array element of the value of this + * shader attribute to the specified value. + */ + abstract void set(int index, Object value); + } + + // Wrapper class for Integer + static class IntegerArrayWrapper extends ArrayWrapper { + private int[] value = new int[0]; + + @Override + void set(Object value) { + Integer[] arr = (Integer[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new int[this.length]; + } + for (int i = 0; i < this.length; i++) { + this.value[i] = arr[i].intValue(); + } + } + + @Override + void set(int index, Object value) { + this.value[index] = ((Integer)value).intValue(); + } + + @Override + Object get() { + Integer[] arr = new Integer[this.length]; + for (int i = 0; i < this.length; i++) { + arr[i] = new Integer(this.value[i]); + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + // Wrapper class for Float + static class FloatArrayWrapper extends ArrayWrapper { + private float[] value = new float[0]; + + @Override + void set(Object value) { + Float[] arr = (Float[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new float[this.length]; + } + for (int i = 0; i < this.length; i++) { + this.value[i] = arr[i].floatValue(); + } + } + + @Override + void set(int index, Object value) { + this.value[index] = ((Float)value).floatValue(); + } + + @Override + Object get() { + Float[] arr = new Float[this.length]; + for (int i = 0; i < this.length; i++) { + arr[i] = new Float(this.value[i]); + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Double + static class DoubleArrayWrapper extends ArrayWrapper { + private double[] value = new double[0]; + + void set(Object value) { + Double[] arr = (Double[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new double[this.length]; + } + for (int i = 0; i < this.length; i++) { + this.value[i] = arr[i].doubleValue(); + } + } + + void set(int index, Object value) { + this.value[index] = ((Double)value).doubleValue(); + } + + Object get() { + Double[] arr = new Double[this.length]; + for (int i = 0; i < this.length; i++) { + arr[i] = new Double(this.value[i]); + } + return arr; + } + + Object getRef() { + return this.value; + } + } + */ + + // Wrapper class for Tuple2i + static class Tuple2iArrayWrapper extends ArrayWrapper { + private int[] value = new int[0]; + + @Override + void set(Object value) { + Tuple2i[] arr = (Tuple2i[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new int[this.length*2]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 2; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + } + } + + @Override + void set(int index, Object value) { + int j = index * 2; + this.value[j+0] = ((Tuple2i)value).x; + this.value[j+1] = ((Tuple2i)value).y; + } + + @Override + Object get() { + Tuple2i[] arr = new Tuple2i[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 2; + arr[i] = new Point2i(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + // Wrapper class for Tuple2f + static class Tuple2fArrayWrapper extends ArrayWrapper { + private float[] value = new float[0]; + + @Override + void set(Object value) { + Tuple2f[] arr = (Tuple2f[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new float[this.length*2]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 2; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + } + } + + @Override + void set(int index, Object value) { + int j = index * 2; + this.value[j+0] = ((Tuple2f)value).x; + this.value[j+1] = ((Tuple2f)value).y; + } + + @Override + Object get() { + Tuple2f[] arr = new Tuple2f[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 2; + arr[i] = new Point2f(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Tuple2d + static class Tuple2dArrayWrapper extends ArrayWrapper { + private double[] value = new double[0]; + + void set(Object value) { + Tuple2d[] arr = (Tuple2d[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new double[this.length*2]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 2; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + } + } + + void set(int index, Object value) { + int j = index * 2; + this.value[j+0] = ((Tuple2d)value).x; + this.value[j+1] = ((Tuple2d)value).y; + } + + Object get() { + Tuple2d[] arr = new Tuple2d[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 2; + arr[i] = new Point2d(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + } + return arr; + } + + Object getRef() { + return this.value; + } + } + */ + + // Wrapper class for Tuple3i + static class Tuple3iArrayWrapper extends ArrayWrapper { + private int[] value = new int[0]; + + @Override + void set(Object value) { + Tuple3i[] arr = (Tuple3i[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new int[this.length*3]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 3; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + this.value[j+2] = arr[i].z; + } + } + + @Override + void set(int index, Object value) { + int j = index * 3; + this.value[j+0] = ((Tuple3i)value).x; + this.value[j+1] = ((Tuple3i)value).y; + this.value[j+2] = ((Tuple3i)value).z; + } + + @Override + Object get() { + Tuple3i[] arr = new Tuple3i[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 3; + arr[i] = new Point3i(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + arr[i].z = this.value[j+2]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + // Wrapper class for Tuple3f + static class Tuple3fArrayWrapper extends ArrayWrapper { + private float[] value = new float[0]; + + @Override + void set(Object value) { + Tuple3f[] arr = (Tuple3f[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new float[this.length*3]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 3; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + this.value[j+2] = arr[i].z; + } + } + + @Override + void set(int index, Object value) { + int j = index * 3; + this.value[j+0] = ((Tuple3f)value).x; + this.value[j+1] = ((Tuple3f)value).y; + this.value[j+2] = ((Tuple3f)value).z; + } + + @Override + Object get() { + Tuple3f[] arr = new Tuple3f[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 3; + arr[i] = new Point3f(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + arr[i].z = this.value[j+2]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Tuple3d + static class Tuple3dArrayWrapper extends ArrayWrapper { + private double[] value = new double[0]; + + void set(Object value) { + Tuple3d[] arr = (Tuple3d[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new double[this.length*3]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 3; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + this.value[j+2] = arr[i].z; + } + } + + void set(int index, Object value) { + int j = index * 3; + this.value[j+0] = ((Tuple3d)value).x; + this.value[j+1] = ((Tuple3d)value).y; + this.value[j+2] = ((Tuple3d)value).z; + } + + Object get() { + Tuple3d[] arr = new Tuple3d[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 3; + arr[i] = new Point3d(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + arr[i].z = this.value[j+2]; + } + return arr; + } + + Object getRef() { + return this.value; + } + } + */ + + // Wrapper class for Tuple4i + static class Tuple4iArrayWrapper extends ArrayWrapper { + private int[] value = new int[0]; + + @Override + void set(Object value) { + Tuple4i[] arr = (Tuple4i[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new int[this.length*4]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 4; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + this.value[j+2] = arr[i].z; + this.value[j+3] = arr[i].w; + } + } + + @Override + void set(int index, Object value) { + int j = index * 4; + this.value[j+0] = ((Tuple4i)value).x; + this.value[j+1] = ((Tuple4i)value).y; + this.value[j+2] = ((Tuple4i)value).z; + this.value[j+3] = ((Tuple4i)value).w; + } + + @Override + Object get() { + Tuple4i[] arr = new Tuple4i[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 4; + arr[i] = new Point4i(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + arr[i].z = this.value[j+2]; + arr[i].w = this.value[j+3]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + // Wrapper class for Tuple4f + static class Tuple4fArrayWrapper extends ArrayWrapper { + private float[] value = new float[0]; + + @Override + void set(Object value) { + Tuple4f[] arr = (Tuple4f[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new float[this.length*4]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 4; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + this.value[j+2] = arr[i].z; + this.value[j+3] = arr[i].w; + } + } + + @Override + void set(int index, Object value) { + int j = index * 4; + this.value[j+0] = ((Tuple4f)value).x; + this.value[j+1] = ((Tuple4f)value).y; + this.value[j+2] = ((Tuple4f)value).z; + this.value[j+3] = ((Tuple4f)value).w; + } + + @Override + Object get() { + Tuple4f[] arr = new Tuple4f[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 4; + arr[i] = new Point4f(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + arr[i].z = this.value[j+2]; + arr[i].w = this.value[j+3]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Tuple4d + static class Tuple4dArrayWrapper extends ArrayWrapper { + private double[] value = new double[0]; + + void set(Object value) { + Tuple4d[] arr = (Tuple4d[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new double[this.length*4]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 4; + this.value[j+0] = arr[i].x; + this.value[j+1] = arr[i].y; + this.value[j+2] = arr[i].z; + this.value[j+3] = arr[i].w; + } + } + + void set(int index, Object value) { + int j = index * 4; + this.value[j+0] = ((Tuple4d)value).x; + this.value[j+1] = ((Tuple4d)value).y; + this.value[j+2] = ((Tuple4d)value).z; + this.value[j+3] = ((Tuple4d)value).w; + } + + Object get() { + Tuple4d[] arr = new Tuple4d[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 4; + arr[i] = new Point4d(); + arr[i].x = this.value[j+0]; + arr[i].y = this.value[j+1]; + arr[i].z = this.value[j+2]; + arr[i].w = this.value[j+3]; + } + return arr; + } + + Object getRef() { + return this.value; + } + } + */ + + // Wrapper class for Matrix3f + static class Matrix3fArrayWrapper extends ArrayWrapper { + private float[] value = new float[0]; + + @Override + void set(Object value) { + Matrix3f[] arr = (Matrix3f[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new float[this.length * 9]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 9; + this.value[j+0] = arr[i].m00; + this.value[j+1] = arr[i].m01; + this.value[j+2] = arr[i].m02; + this.value[j+3] = arr[i].m10; + this.value[j+4] = arr[i].m11; + this.value[j+5] = arr[i].m12; + this.value[j+6] = arr[i].m20; + this.value[j+7] = arr[i].m21; + this.value[j+8] = arr[i].m22; + } + } + + @Override + void set(int index, Object value) { + int j = index * 9; + Matrix3f m = (Matrix3f)value; + + this.value[j+0] = m.m00; + this.value[j+1] = m.m01; + this.value[j+2] = m.m02; + this.value[j+3] = m.m10; + this.value[j+4] = m.m11; + this.value[j+5] = m.m12; + this.value[j+6] = m.m20; + this.value[j+7] = m.m21; + this.value[j+8] = m.m22; + } + + @Override + Object get() { + Matrix3f[] arr = new Matrix3f[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 9; + arr[i] = new Matrix3f(); + arr[i].m00 = this.value[j+0]; + arr[i].m01 = this.value[j+1]; + arr[i].m02 = this.value[j+2]; + arr[i].m10 = this.value[j+3]; + arr[i].m11 = this.value[j+4]; + arr[i].m12 = this.value[j+5]; + arr[i].m20 = this.value[j+6]; + arr[i].m21 = this.value[j+7]; + arr[i].m22 = this.value[j+8]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Matrix3d + static class Matrix3dArrayWrapper extends ArrayWrapper { + private double[] value = new double[0]; + + void set(Object value) { + Matrix3d[] arr = (Matrix3d[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new double[this.length * 9]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 9; + this.value[j+0] = arr[i].m00; + this.value[j+1] = arr[i].m01; + this.value[j+2] = arr[i].m02; + this.value[j+3] = arr[i].m10; + this.value[j+4] = arr[i].m11; + this.value[j+5] = arr[i].m12; + this.value[j+6] = arr[i].m20; + this.value[j+7] = arr[i].m21; + this.value[j+8] = arr[i].m22; + } + } + + void set(int index, Object value) { + int j = index * 9; + Matrix3d m = (Matrix3d)value; + + this.value[j+0] = m.m00; + this.value[j+1] = m.m01; + this.value[j+2] = m.m02; + this.value[j+3] = m.m10; + this.value[j+4] = m.m11; + this.value[j+5] = m.m12; + this.value[j+6] = m.m20; + this.value[j+7] = m.m21; + this.value[j+8] = m.m22; + } + + Object get() { + Matrix3d[] arr = new Matrix3d[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 9; + arr[i] = new Matrix3d(); + arr[i].m00 = this.value[j+0]; + arr[i].m01 = this.value[j+1]; + arr[i].m02 = this.value[j+2]; + arr[i].m10 = this.value[j+3]; + arr[i].m11 = this.value[j+4]; + arr[i].m12 = this.value[j+5]; + arr[i].m20 = this.value[j+6]; + arr[i].m21 = this.value[j+7]; + arr[i].m22 = this.value[j+8]; + } + return arr; + } + + Object getRef() { + return this.value; + } + } + */ + + // Wrapper class for Matrix4f + static class Matrix4fArrayWrapper extends ArrayWrapper { + private float[] value = new float[0]; + + @Override + void set(Object value) { + Matrix4f[] arr = (Matrix4f[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new float[this.length * 16]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 16; + this.value[j+0] = arr[i].m00; + this.value[j+1] = arr[i].m01; + this.value[j+2] = arr[i].m02; + this.value[j+3] = arr[i].m03; + this.value[j+4] = arr[i].m10; + this.value[j+5] = arr[i].m11; + this.value[j+6] = arr[i].m12; + this.value[j+7] = arr[i].m13; + this.value[j+8] = arr[i].m20; + this.value[j+9] = arr[i].m21; + this.value[j+10] = arr[i].m22; + this.value[j+11] = arr[i].m23; + this.value[j+12] = arr[i].m30; + this.value[j+13] = arr[i].m31; + this.value[j+14] = arr[i].m32; + this.value[j+15] = arr[i].m33; + } + } + + @Override + void set(int index, Object value) { + int j = index * 16; + Matrix4f m = (Matrix4f)value; + + this.value[j+0] = m.m00; + this.value[j+1] = m.m01; + this.value[j+2] = m.m02; + this.value[j+3] = m.m03; + this.value[j+4] = m.m10; + this.value[j+5] = m.m11; + this.value[j+6] = m.m12; + this.value[j+7] = m.m13; + this.value[j+8] = m.m20; + this.value[j+9] = m.m21; + this.value[j+10] = m.m22; + this.value[j+11] = m.m23; + this.value[j+12] = m.m30; + this.value[j+13] = m.m31; + this.value[j+14] = m.m32; + this.value[j+15] = m.m33; + } + + @Override + Object get() { + Matrix4f[] arr = new Matrix4f[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 16; + arr[i] = new Matrix4f(); + arr[i].m00 = this.value[j+0]; + arr[i].m01 = this.value[j+1]; + arr[i].m02 = this.value[j+2]; + arr[i].m03 = this.value[j+3]; + arr[i].m10 = this.value[j+4]; + arr[i].m11 = this.value[j+5]; + arr[i].m12 = this.value[j+6]; + arr[i].m13 = this.value[j+7]; + arr[i].m20 = this.value[j+8]; + arr[i].m21 = this.value[j+9]; + arr[i].m22 = this.value[j+10]; + arr[i].m23 = this.value[j+11]; + arr[i].m30 = this.value[j+12]; + arr[i].m31 = this.value[j+13]; + arr[i].m32 = this.value[j+14]; + arr[i].m33 = this.value[j+15]; + } + return arr; + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Matrix4d + static class Matrix4dArrayWrapper extends ArrayWrapper { + private double[] value = new double[0]; + + void set(Object value) { + Matrix4d[] arr = (Matrix4d[])value; + if (this.length != arr.length) { + this.length = arr.length; + this.value = new double[this.length * 16]; + } + for (int i = 0; i < this.length; i++) { + int j = i * 16; + this.value[j+0] = arr[i].m00; + this.value[j+1] = arr[i].m01; + this.value[j+2] = arr[i].m02; + this.value[j+3] = arr[i].m03; + this.value[j+4] = arr[i].m10; + this.value[j+5] = arr[i].m11; + this.value[j+6] = arr[i].m12; + this.value[j+7] = arr[i].m13; + this.value[j+8] = arr[i].m20; + this.value[j+9] = arr[i].m21; + this.value[j+10] = arr[i].m22; + this.value[j+11] = arr[i].m23; + this.value[j+12] = arr[i].m30; + this.value[j+13] = arr[i].m31; + this.value[j+14] = arr[i].m32; + this.value[j+15] = arr[i].m33; + } + } + + void set(int index, Object value) { + int j = index * 16; + Matrix4d m = (Matrix4d)value; + + this.value[j+0] = m.m00; + this.value[j+1] = m.m01; + this.value[j+2] = m.m02; + this.value[j+3] = m.m03; + this.value[j+4] = m.m10; + this.value[j+5] = m.m11; + this.value[j+6] = m.m12; + this.value[j+7] = m.m13; + this.value[j+8] = m.m20; + this.value[j+9] = m.m21; + this.value[j+10] = m.m22; + this.value[j+11] = m.m23; + this.value[j+12] = m.m30; + this.value[j+13] = m.m31; + this.value[j+14] = m.m32; + this.value[j+15] = m.m33; + } + + Object get() { + Matrix4d[] arr = new Matrix4d[this.length]; + for (int i = 0; i < this.length; i++) { + int j = i * 16; + arr[i] = new Matrix4d(); + arr[i].m00 = this.value[j+0]; + arr[i].m01 = this.value[j+1]; + arr[i].m02 = this.value[j+2]; + arr[i].m03 = this.value[j+3]; + arr[i].m10 = this.value[j+4]; + arr[i].m11 = this.value[j+5]; + arr[i].m12 = this.value[j+6]; + arr[i].m13 = this.value[j+7]; + arr[i].m20 = this.value[j+8]; + arr[i].m21 = this.value[j+9]; + arr[i].m22 = this.value[j+10]; + arr[i].m23 = this.value[j+11]; + arr[i].m30 = this.value[j+12]; + arr[i].m31 = this.value[j+13]; + arr[i].m32 = this.value[j+14]; + arr[i].m33 = this.value[j+15]; + } + return arr; + } + + Object getRef() { + return this.value; + } + } + */ +} diff --git a/src/javax/media/j3d/ShaderAttributeBinding.java b/src/javax/media/j3d/ShaderAttributeBinding.java new file mode 100644 index 0000000..f0359af --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeBinding.java @@ -0,0 +1,142 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeBinding object encapsulates a uniform attribute + * whose value is bound to a Java 3D system attribute. The + * shader variable attrName is implicitly set to the + * value of the corresponding Java 3D system attribute + * j3dAttrName during rendering. attrName + * must be the name of a valid uniform attribute in the shader in + * which it is used. Otherwise, the attribute name will be ignored and + * a runtime error may be generated. j3dAttrName must be + * the name of a predefined Java 3D system attribute. An + * IllegalArgumentException will be thrown if the specified + * j3dAttrName is not one of the predefined system + * attributes. Further, the type of the j3dAttrName + * attribute must match the type of the corresponding + * attrName variable in the shader in which it is + * used. Otherwise, the shader will not be able to use the attribute + * and a runtime error may be generated. + * + *

+ * Following is the list of predefined Java 3D system attributes:
+ * + *

    + * TODO: replace the following with + * the real system attributes table
    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    NameTypeDescription
    somethingFloatThis is something (of course)
    somethingElseTuple3fThis is something else
    + *
+ * + *

+ * Depending on the shading language (and profile) being used, several + * Java 3D state attributes are automatically made available to the + * shader program as pre-defined uniform attributes. The application + * doesn't need to do anything to pass these attributes in to the + * shader program. The implementation of each shader language (e.g., + * Cg, GLSL) defines its own mapping from Java 3D attribute to uniform + * variable name. + * + *

+ * A list of these attributes for each shader language can be found in + * the concrete subclass of ShaderProgram for that shader language. + * + *

+ * NOTE: This class is not yet + * implemented.
+ * + * @see ShaderAttributeSet + * @see ShaderProgram + * + * @since Java 3D 1.4 + */ + +public class ShaderAttributeBinding extends ShaderAttribute { + + /** + * Constructs a new ShaderAttributeBinding from the specified + * (attrName, j3dAttrName) pair. + * + * @param attrName the name of the shader attribute to be added + * @param j3dAttrName the name of the Java 3D attribute + * to bind to the shader attribute + * + * @exception UnsupportedOperationException this class is not + * yet implemented + * + * @exception NullPointerException if attrName or j3dAttrName is null + * + * @exception IllegalArgumentException if j3dAttrName is not the name + * of a valid predefined Java 3D system attribute + */ + public ShaderAttributeBinding(String attrName, String j3dAttrName) { + super(attrName); + ((ShaderAttributeBindingRetained)this.retained).initJ3dAttrName(j3dAttrName); + // TODO: implement this class + throw new UnsupportedOperationException(J3dI18N.getString("ShaderAttributeBinding0")); + } + + /** + * Retrieves the name of the Java 3D system attribute that is bound to this + * shader attribute. + * + * @return the name of the Java 3D system attribute that is bound to this + * shader attribute + */ + public String getJ3DAttributeName() { + return ((ShaderAttributeBindingRetained)this.retained).getJ3DAttributeName(); + } + + /** + * Creates a retained mode ShaderAttributeBindingRetained object that this + * ShaderAttributeBinding component object will point to. + */ + @Override + void createRetained() { + this.retained = new ShaderAttributeBindingRetained(); + this.retained.setSource(this); + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeBindingRetained.java b/src/javax/media/j3d/ShaderAttributeBindingRetained.java new file mode 100644 index 0000000..d11d5cc --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeBindingRetained.java @@ -0,0 +1,70 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeBinding object encapsulates a uniform attribute + * whose value is bound to a Java 3D system attribute. The + * shader variable attrName is implicitly set to the + * value of the corresponding Java 3D system attribute + * j3dAttrName during rendering. attrName + * must be the name of a valid uniform attribute in the shader in + * which it is used. Otherwise, the attribute name will be ignored and + * a runtime error may be generated. j3dAttrName must be + * the name of a predefined Java 3D system attribute. An + * IllegalArgumentException will be thrown if the specified + * j3dAttrName is not one of the predefined system + * attributes. Further, the type of the j3dAttrName + * attribute must match the type of the corresponding + * attrName variable in the shader in which it is + * used. Otherwise, the shader will not be able to use the attribute + * and a runtime error may be generated. + */ + +class ShaderAttributeBindingRetained extends ShaderAttributeRetained { + String j3dAttrName; + + ShaderAttributeBindingRetained() { + } + + void initJ3dAttrName(String j3dAttrName) { + this.j3dAttrName = j3dAttrName; + } + + /** + * Retrieves the name of the Java 3D system attribute that is bound to this + * shader attribute. + * + * @return the name of the Java 3D system attribute that is bound to this + * shader attribute + */ + String getJ3DAttributeName() { + return j3dAttrName; + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeObject.java b/src/javax/media/j3d/ShaderAttributeObject.java new file mode 100644 index 0000000..8dc7125 --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeObject.java @@ -0,0 +1,143 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeObject class is an abstract class that + * encapsulates a uniform shader attribute whose value is specified + * explicitly. This class has concrete subclasses for single-value + * attributes (ShaderAttributeValue) and array attributes + * (ShaderAttributeArray). The shader variable attrName + * is explicitly set to the specified value during + * rendering. attrName must be the name of a valid + * uniform attribute in the shader in which it is used. Otherwise, the + * attribute name will be ignored and a runtime error may be + * generated. The value must be an instance of one of the + * allowed classes or an array of one the allowed classes. The allowed + * classes are: Integer, Float, + * Tuple{2,3,4}{i,f}, + * Matrix{3,4}f. A ClassCastException will be thrown + * if a specified value object is not one of the allowed + * types. Further, the type of the value is immutable once a + * ShaderAttributeObject is constructed. Subsequent setValue + * operations must be called with an object of the same type as the + * one that was used to construct the ShaderAttributeObject. Finally, + * the type of the value object must match the type of + * the corresponding attrName variable in the shader in + * which it is used. Otherwise, the shader will not be able to use the + * attribute and a runtime error may be generated. + * + * @see ShaderAttributeSet + * @see ShaderProgram + * + * @since Java 3D 1.4 + */ + +public abstract class ShaderAttributeObject extends ShaderAttribute { + + /** + * Specifies that this ShaderAttributeObject allows reading its value. + */ + public static final int + ALLOW_VALUE_READ = + CapabilityBits.SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_READ; + + /** + * Specifies that this ShaderAttributeObject allows writing its value. + */ + public static final int + ALLOW_VALUE_WRITE = + CapabilityBits.SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_WRITE; + + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_VALUE_READ + }; + + + /** + * Package scope constructor + */ + ShaderAttributeObject(String attrName, Object value) { + super(attrName); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((ShaderAttributeObjectRetained)this.retained).createObjectData(value); + } + + + /** + * Retrieves the value of this shader attribute. + * A copy of the object is returned. + * + * @return a copy of the value of this shader attribute + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public abstract Object getValue(); + + /** + * Sets the value of this shader attribute to the specified value. + * A copy of the object is stored. + * + * @param value the new value of the shader attribute + * + * @exception NullPointerException if value is null + * + * @exception ClassCastException if value is not an instance of + * the same base class as the object used to construct this shader + * attribute object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public abstract void setValue(Object value); + + /** + * Retrieves the base class of the value of this shader attribute. + * This class will always be one of the allowable classes, even if + * a subclass was used to construct this shader attribute object. + * For example, if this shader attribute object was constructed + * with an instance of javax.vecmath.Point3f, the + * returned class would be javax.vecmath.Tuple3f. + * + * @return the base class of the value of this shader attribute + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public Class getValueClass() { + + return ((ShaderAttributeObjectRetained)this.retained).getValueClass(); + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeObjectRetained.java b/src/javax/media/j3d/ShaderAttributeObjectRetained.java new file mode 100644 index 0000000..eb5d8f7 --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeObjectRetained.java @@ -0,0 +1,332 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4f; +import javax.vecmath.Tuple2f; +import javax.vecmath.Tuple2i; +import javax.vecmath.Tuple3f; +import javax.vecmath.Tuple3i; +import javax.vecmath.Tuple4f; +import javax.vecmath.Tuple4i; + +/** + * The ShaderAttributeObjectRetained class is an abstract class that + * encapsulates a uniform shader attribute whose value is specified + * explicitly. + */ + +abstract class ShaderAttributeObjectRetained extends ShaderAttributeRetained { + + private int classType; + private Class baseClass; + AttrWrapper attrWrapper; + + /** + * Package scope constructor + */ + ShaderAttributeObjectRetained() { + } + + void createObjectData(Object value) { + + classType = computeClassType(value); + baseClass = getBaseClass(classType); + attrWrapper = createAttrWrapper(value, classType); + /* + System.err.println(" classType = " + classType + + ", baseClass = " + baseClass + + ", attrWrapper.get() = " + attrWrapper.get()); + */ + } + + + void initValue(Object value) { + /* + System.err.println("ShaderAttributeObjectRetained : attrName = " + attrName + + ", value = " + value + + ", value.class = " + value.getClass()); + */ + attrWrapper.set(value); + + } + + /** + * Retrieves the value of this shader attribute. + * A copy of the object is returned. + */ + Object getValue() { + return attrWrapper.get(); + } + + /** + * Sets the value of this shader attribute to the specified value. + * A copy of the object is stored. + * + * @param value the new value of the shader attribute + * + * @exception NullPointerException if value is null + * + * @exception ClassCastException if value is not an instance of + * the same base class as the object used to construct this shader + * attribute object. + * + */ + void setValue(Object value) { + initValue(value); + AttrWrapper valueWrapper = createAttrWrapper(value, this.classType); + sendMessage(ShaderConstants.ATTRIBUTE_VALUE_UPDATE, valueWrapper); + } + + /** + * Retrieves the base class of the value of this shader attribute. + * This class will always be one of the allowable classes, even if + * a subclass was used to construct this shader attribute object. + * For example, if this shader attribute object was constructed + * with an instance of javax.vecmath.Point3f, the + * returned class would be javax.vecmath.Tuple3f. + * + * @return the base class of the value of this shader attribute + */ + Class getValueClass() { + return baseClass; + } + + /** + * Initializes a mirror object. + */ + @Override + synchronized void initMirrorObject() { + super.initMirrorObject(); + ((ShaderAttributeObjectRetained)mirror).initValue(getValue()); + } + + /** + * Update the "component" field of the mirror object with the given "value" + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + + //System.err.println("ShaderAttributeObjectRetained : updateMirrorObject"); + ShaderAttributeObjectRetained mirrorSAV = (ShaderAttributeObjectRetained)mirror; + if ((component & ShaderConstants.ATTRIBUTE_VALUE_UPDATE) != 0) { + //System.err.println(" -- SHADER_ATTRIBUTE_VALUE_UPDATE"); + mirrorSAV.attrWrapper = (AttrWrapper) value; + } + } + + 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 = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES; + createMessage.type = J3dMessage.SHADER_ATTRIBUTE_CHANGED; + createMessage.universe = null; + createMessage.args[0] = this; + createMessage.args[1]= new Integer(attrMask); + createMessage.args[2] = attr; + // System.err.println("changedFreqent1 = "+changedFrequent); + createMessage.args[3] = new Integer(changedFrequent); + VirtualUniverse.mc.processMessage(createMessage); + + // System.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + + // Enumerated types representing allowed classes for shader + // attributes. + // + // NOTE that the values for these enums are used as an index into + // the tables of classes, so the values must start at 0 and + // increment by 1. Also, the order must be the same as the order + // of the entries in each of the two class tables. + static final int TYPE_INTEGER = 0; + static final int TYPE_FLOAT = 1; + static final int TYPE_TUPLE2I = 2; + static final int TYPE_TUPLE2F = 3; + static final int TYPE_TUPLE3I = 4; + static final int TYPE_TUPLE3F = 5; + static final int TYPE_TUPLE4I = 6; + static final int TYPE_TUPLE4F = 7; + static final int TYPE_MATRIX3F = 8; + static final int TYPE_MATRIX4F = 9; + + // Double-precision is not supported in the current version. Uncomment the + // following if future support is done. +// static final int TYPE_DOUBLE = 10; +// static final int TYPE_TUPLE2D = 11; +// static final int TYPE_TUPLE3D = 12; +// static final int TYPE_TUPLE4D = 13; +// static final int TYPE_MATRIX3D = 14; +// static final int TYPE_MATRIX4D = 15; + + static final Class classTable[] = { + Integer.class, + Float.class, + Tuple2i.class, + Tuple2f.class, + Tuple3i.class, + Tuple3f.class, + Tuple4i.class, + Tuple4f.class, + Matrix3f.class, + Matrix4f.class, + + // Double-precision is not supported in the current version. Uncomment the + // following if future support is done. +// Double.class, +// Tuple2d.class, +// Tuple3d.class, +// Tuple4d.class, +// Matrix3d.class, +// Matrix4d.class, + }; + + static final Class classTableArr[] = { + Integer[].class, + Float[].class, + Tuple2i[].class, + Tuple2f[].class, + Tuple3i[].class, + Tuple3f[].class, + Tuple4i[].class, + Tuple4f[].class, + Matrix3f[].class, + Matrix4f[].class, + + // Double-precision is not supported in the current version. Uncomment the + // following if future support is done. +// Double[].class, +// Tuple2d[].class, +// Tuple3d[].class, +// Tuple4d[].class, +// Matrix3d[].class, +// Matrix4d[].class, + }; + + + /** + * Computes the base class from the specified object. A + * ClassCastException is thrown if the object is not an instance + * or array of one of the allowed classes. + */ + abstract int computeClassType(Object value); + + /** + * Returns the base class represented by the specified class type. + */ + abstract Class getBaseClass(int classType); + + /** + * Creates an attribute wrapper object of the specified class + * type, and stores the specified object. + */ + abstract AttrWrapper createAttrWrapper(Object value, int classType); + + + /** + * Base wrapper class for subclasses that are used to store a copy + * of the user-specified shader attribute value. There is a + * wrapper class for each supported base class in ShaderAttributeValue + * and ShaderAttributeArray. The value is stored in a Java primitive array. + */ + static abstract class AttrWrapper { + /** + * Stores a copy of the specified object in the wrapper object + */ + abstract void set(Object value); + + /** + * Returns a copy of the wrapped object + */ + abstract Object get(); + + /** + * Returns a reference to the internal primitive array used to + * wrap the object; note that the caller of this method must + * treat the data as read-only. It is intended only as a means + * to pass data down to native methods. + */ + abstract Object getRef(); + } + + int getClassType() { + return classType; + } + + void setClassType(int classType) { + this.classType = classType; + } + + + // Issue 320 : Override base class method so we can force changedFrequent + // to be set whenever the capability is writable, regardless of whether + // it is frequently writable. We must do this because the ShaderBin doesn't + // support updating shader attributes when changedFrequent is 0. + @Override + void setFrequencyChangeMask(int bit, int mask) { + if (source.getCapability(bit)) { + changedFrequent |= mask; + } else if (!source.isLive()) { + // Record the freq->infreq change only for non-live node components + changedFrequent &= ~mask; + } + } + + @Override + void handleFrequencyChange(int bit) { + if (bit == ShaderAttributeObject.ALLOW_VALUE_WRITE) { + setFrequencyChangeMask(bit, 0x1); + } + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeRetained.java b/src/javax/media/j3d/ShaderAttributeRetained.java new file mode 100644 index 0000000..3e65fec --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeRetained.java @@ -0,0 +1,66 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeRetained object encapsulates a uniform attribute for a + * shader programs. + */ + +abstract class ShaderAttributeRetained extends NodeComponentRetained { + + /** + * Name of the shader attribute (immutable) + */ + String attrName; + + /** + * Package scope constructor + */ + ShaderAttributeRetained() { + } + + void initializeAttrName(String attrName) { + this.attrName = attrName; + } + + /** + * Retrieves the name of this shader attribute. + * + * @return the name of this shader attribute + */ + String getAttributeName() { + return attrName; + } + + @Override + void initMirrorObject() { + ((ShaderAttributeObjectRetained)mirror).initializeAttrName(this.attrName); + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeSet.java b/src/javax/media/j3d/ShaderAttributeSet.java new file mode 100644 index 0000000..2503626 --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeSet.java @@ -0,0 +1,274 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeSet object provides uniform attributes to shader + * programs. Uniform attributes (variables) are those attributes whose + * values are constant during the rendering of a primitive. Their + * values may change from primitive to primitive, but are constant for + * each vertex (for vertex shaders) or fragment (for fragment shaders) + * of a single primitive. Examples of uniform attributes include a + * transformation matrix, a texture map, lights, lookup tables, etc. + * The ShaderAttributeSet object contains a set of ShaderAttribute + * objects. Each ShaderAttribute object defines the value of a single + * uniform shader variable. The set of attributes is unique with respect + * to attribute names: no two attributes in the set will have the same + * name. + * + *

+ * There are two ways in which values can be specified for uniform + * attributes: explicitly, by providing a value; and implicitly, by + * defining a binding between a Java 3D system attribute and a uniform + * attribute. This functionality is provided by two subclasses of + * ShaderAttribute: ShaderAttributeObject, which is used to specify + * explicitly defined attributes; and ShaderAttributeBinding, which is + * used to specify implicitly defined, automatically tracked attributes. + * + *

+ * Depending on the shading language (and profile) being used, several + * Java 3D state attributes are automatically made available to the + * shader program as pre-defined uniform attributes. The application + * doesn't need to do anything to pass these attributes in to the + * shader program. The implementation of each shader language (e.g., + * Cg, GLSL) defines its own bindings from Java 3D attribute to uniform + * variable name. A list of these attributes for each shader language + * can be found in the concrete subclass of ShaderProgram for that + * shader language. + * + * @see ShaderAttribute + * @see ShaderProgram + * @see ShaderAppearance#setShaderAttributeSet + * + * @since Java 3D 1.4 + */ + +public class ShaderAttributeSet extends NodeComponent { + + /** + * Specifies that this ShaderAttributeSet object allows reading + * its attributes. + */ + public static final int + ALLOW_ATTRIBUTES_READ = + CapabilityBits.SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_READ; + + /** + * Specifies that this ShaderAttributeSet object allows writing + * its attributes. + */ + public static final int + ALLOW_ATTRIBUTES_WRITE = + CapabilityBits.SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_WRITE; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_ATTRIBUTES_READ + }; + + /** + * Constructs an empty ShaderAttributeSet object. The attributes set + * is initially empty. + */ + public ShaderAttributeSet() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + // + // Methods for dealing with the (name, value) pairs for explicit + // attributes + // + + /** + * Adds the specified shader attribute to the attributes set. + * The newly specified attribute replaces an attribute with the + * same name, if one already exists in the attributes set. + * + * @param attr the shader attribute to be added to the set + * + * @exception NullPointerException if attr is null + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void put(ShaderAttribute attr) { + if (attr == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1")); + + ((ShaderAttributeSetRetained)this.retained).put(attr); + + } + + /** + * Retrieves the shader attribute with the specified + * attrName from the attributes set. If attrName does + * not exist in the attributes set, null is returned. + * + * @param attrName the name of the shader attribute to be retrieved + * + * @exception NullPointerException if attrName is null + * + * @return a the shader attribute associated with the specified + * attribute name, or null if the name is not in the attributes + * set + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public ShaderAttribute get(String attrName) { + + if (attrName == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet0")); + + return ((ShaderAttributeSetRetained)this.retained).get(attrName); + } + + /** + * Removes the shader attribute with the specified + * attrName from the attributes set. If attrName does + * not exist in the attributes set then nothing happens. + * + * @param attrName the name of the shader attribute to be removed + * + * @exception NullPointerException if attrName is null + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void remove(String attrName) { + if (attrName == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1")); + + ((ShaderAttributeSetRetained)this.retained).remove(attrName); + } + + /** + * Removes the specified shader attribute from the attributes + * set. If the attribute does not exist in the attributes set then + * nothing happens. Note that this method will not remove a + * shader object other than the one specified, even if it has the + * same name as the specified attribute. Applications that wish to + * remove an attribute by name should use + * removeAttribute(String). + * + * @param attr the shader attribute to be removed + * + * @exception NullPointerException if attr is null + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void remove(ShaderAttribute attr) { + if (attr == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1")); + + ((ShaderAttributeSetRetained)this.retained).remove(attr); + } + + /** + * Removes all shader attributes from the attributes set. The + * attributes set will be empty following this call. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public void clear() { + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1")); + + ((ShaderAttributeSetRetained)this.retained).clear(); + } + + /** + * Returns a shallow copy of the attributes set. + * + * @return a shallow copy of the attributes set + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public ShaderAttribute[] getAll() { + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet0")); + + return ((ShaderAttributeSetRetained)this.retained).getAll(); + } + + /** + * Returns the number of elements in the attributes set. + * + * @return the number of elements in the attributes set + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public int size() { + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_ATTRIBUTES_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet0")); + + return ((ShaderAttributeSetRetained)this.retained).size(); + } + + /** + * Creates a retained mode ShaderAttributeSetRetained object that this + * ShaderAttributeSet component object will point to. + */ + @Override + void createRetained() { + // System.err.println("ShaderAttributeSet : createRetained() ..."); + this.retained = new ShaderAttributeSetRetained(); + this.retained.setSource(this); + } +} diff --git a/src/javax/media/j3d/ShaderAttributeSetRetained.java b/src/javax/media/j3d/ShaderAttributeSetRetained.java new file mode 100644 index 0000000..2ad4432 --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeSetRetained.java @@ -0,0 +1,391 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + + +/** + * The ShaderAttributeSet object provides uniform attributes to shader + * programs. + */ + +class ShaderAttributeSetRetained extends NodeComponentRetained { + +private Map attrs = new HashMap(); + + // Lock used for synchronization of live state + Object liveStateLock = new Object(); + + /** + * Constructs an empty ShaderAttributeSetretained object. The attributes set + * is initially empty. + */ + ShaderAttributeSetRetained() { + } + + // + // Methods for dealing with the (name, value) pairs for explicit + // attributes + // + + /** + * Adds the specified shader attribute to the attributes set. + * The newly specified attribute replaces an attribute with the + * same name, if one already exists in the attributes set. + * + * @param attr the shader attribute to be added to the set + * + */ + void put(ShaderAttribute attr) { + synchronized(liveStateLock) { + // System.err.println("ShaderAttributeSetRetained : put()"); + ShaderAttributeRetained sAttr = (ShaderAttributeRetained)attr.retained; + // System.err.println("attr is " + attr ); + // System.err.println("attrName is " + sAttr.attrName + " attr.Retained is "+ sAttr ); + assert(sAttr != null); + attrs.put(sAttr.attrName, sAttr); + + if (source.isLive()) { + sAttr.setLive(inBackgroundGroup, refCount); + sAttr.copyMirrorUsers(this); + + sendMessage(ShaderConstants.ATTRIBUTE_SET_PUT, sAttr.mirror); + } + } + } + + /** + * Retrieves the shader attribute with the specified + * attrName from the attributes set. If attrName does + * not exist in the attributes set, null is returned. + * + * @param attrName the name of the shader attribute to be retrieved + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + ShaderAttribute get(String attrName) { + return (ShaderAttribute)attrs.get(attrName).source; + } + + /** + * Removes the shader attribute with the specified + * attrName from the attributes set. If attrName does + * not exist in the attributes set then nothing happens. + * + * @param attrName the name of the shader attribute to be removed + */ + void remove(String attrName) { + synchronized(liveStateLock) { + ShaderAttributeRetained sAttr = attrs.get(attrName); + attrs.remove(attrName); + if (source.isLive()) { + sAttr.clearLive(refCount); + sAttr.removeMirrorUsers(this); + + sendMessage(ShaderConstants.ATTRIBUTE_SET_REMOVE, attrName); + } + } + } + + /** + * Removes the specified shader attribute from the attributes + * set. If the attribute does not exist in the attributes set then + * nothing happens. Note that this method will not remove a + * shader object other than the one specified, even if it has the + * same name as the specified attribute. Applications that wish to + * remove an attribute by name should use + * removeAttribute(String). + * + * @param attr the shader attribute to be removed + */ + void remove(ShaderAttribute attr) { + synchronized(liveStateLock) { + String attrName = attr.getAttributeName(); + if (attrs.get(attrName) == attr.retained) { + attrs.remove(attrName); + if (source.isLive()) { + ((ShaderAttributeRetained)attr.retained).clearLive(refCount); + ((ShaderAttributeRetained)attr.retained).removeMirrorUsers(this); + + sendMessage(ShaderConstants.ATTRIBUTE_SET_REMOVE, attrName); + } + } + } + } + + /** + * Removes all shader attributes from the attributes set. The + * attributes set will be empty following this call. + * + */ + void clear() { + synchronized(liveStateLock) { + attrs.clear(); + if(source.isLive()) { + ShaderAttributeRetained[] sAttrs = new ShaderAttributeRetained[attrs.size()]; + sAttrs = attrs.values().toArray(sAttrs); + for (int i = 0; i < sAttrs.length; i++) { + sAttrs[i].clearLive(refCount); + sAttrs[i].removeMirrorUsers(this); + } + sendMessage(ShaderConstants.ATTRIBUTE_SET_CLEAR, null); + } + } + } + + /** + * Returns a shallow copy of the attributes set. + * + * @return a shallow copy of the attributes set + * + */ + ShaderAttribute[] getAll() { + + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + ShaderAttribute[] sAttrs = new ShaderAttribute[sAttrsRetained.length]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrs[i] = (ShaderAttribute) sAttrsRetained[i].source; + } + + return sAttrs; + } + + /** + * Returns the number of elements in the attributes set. + * + * @return the number of elements in the attributes set + * + */ + int size() { + return attrs.size(); + } + + + void updateNative(Canvas3D cv, ShaderProgramRetained shaderProgram) { + shaderProgram.setShaderAttributes(cv, this); + } + +Map getAttrs() { + return attrs; +} + + @Override + void setLive(boolean backgroundGroup, int refCount) { + + // System.err.println("ShaderAttributeSetRetained.setLive()"); + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrsRetained[i].setLive(backgroundGroup, refCount); + } + + super.doSetLive(backgroundGroup, refCount); + super.markAsLive(); + } + + @Override + synchronized void addAMirrorUser(Shape3DRetained shape) { + + super.addAMirrorUser(shape); + + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrsRetained[i].addAMirrorUser(shape); + } + } + + @Override + synchronized void removeAMirrorUser(Shape3DRetained shape) { + super.removeAMirrorUser(shape); + + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrsRetained[i].removeAMirrorUser(shape); + } + } + + + @Override + synchronized void removeMirrorUsers(NodeComponentRetained node) { + super.removeMirrorUsers(node); + + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrsRetained[i].removeMirrorUsers(node); + } + } + + @Override + synchronized void copyMirrorUsers(NodeComponentRetained node) { + super.copyMirrorUsers(node); + + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrsRetained[i].copyMirrorUsers(node); + } + } + + @Override + void clearLive(int refCount) { + // System.err.println("ShaderAttributeSetRetained.clearLive()"); + + super.clearLive(refCount); + + ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()]; + sAttrsRetained = attrs.values().toArray(sAttrsRetained); + for(int i=0; i < sAttrsRetained.length; i++) { + sAttrsRetained[i].clearLive(refCount); + } + } + + @Override + synchronized void createMirrorObject() { + // System.err.println("ShaderAttributeSetRetained : createMirrorObject"); + // This method should only call by setLive(). + if (mirror == null) { + ShaderAttributeSetRetained mirrorSAS = new ShaderAttributeSetRetained(); + mirror = mirrorSAS; + mirror.source = source; + + } + initMirrorObject(); + } + + @Override + void initMirrorObject() { + + ShaderAttributeRetained[] sAttrs = new ShaderAttributeRetained[attrs.size()]; + sAttrs = attrs.values().toArray(sAttrs); + // Need to copy the mirror attrs + for (int i = 0; i < sAttrs.length; i++) { + ShaderAttributeRetained mirrorSA = (ShaderAttributeRetained) sAttrs[i].mirror; + assert(mirrorSA != null); + ((ShaderAttributeSetRetained)mirror).attrs.put(mirrorSA.attrName, mirrorSA); + } + } + + /** + * Update the "component" field of the mirror object with the given "value" + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + + // System.err.println("ShaderAttributeSetRetained : updateMirrorObject"); + + ShaderAttributeSetRetained mirrorSAS = (ShaderAttributeSetRetained)mirror; + + if ((component & ShaderConstants.ATTRIBUTE_SET_PUT) != 0) { + // System.err.println(" -- ATTRIBUTE_SET_PUT"); + ShaderAttributeRetained mirrorSA = (ShaderAttributeRetained)value; + assert(mirrorSA != null); + ((ShaderAttributeSetRetained)mirror).attrs.put(mirrorSA.attrName, mirrorSA); + } + else if((component & ShaderConstants.ATTRIBUTE_SET_REMOVE) != 0) { + // System.err.println(" -- ATTRIBUTE_SET_REMOVE"); + ((ShaderAttributeSetRetained)mirror).attrs.remove((String)value); + } + else if((component & ShaderConstants.ATTRIBUTE_SET_CLEAR) != 0) { + // System.err.println(" -- ATTRIBUTE_SET_CLEAR"); + ((ShaderAttributeSetRetained)mirror).attrs.clear(); + } + else { + assert(false); + } + } + + 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 = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES; + createMessage.type = J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED; + createMessage.universe = null; + createMessage.args[0] = this; + createMessage.args[1]= new Integer(attrMask); + createMessage.args[2] = attr; + // System.err.println("changedFreqent1 = "+changedFrequent); + createMessage.args[3] = new Integer(changedFrequent); + VirtualUniverse.mc.processMessage(createMessage); + + // System.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + + // Issue 320 : Override base class method so we can force changedFrequent + // to be set whenever the capability is writable, regardless of whether + // it is frequently writable. We must do this because the ShaderBin doesn't + // support updating shader attributes when changedFrequent is 0. + @Override + void setFrequencyChangeMask(int bit, int mask) { + if (source.getCapability(bit)) { + changedFrequent |= mask; + } else if (!source.isLive()) { + // Record the freq->infreq change only for non-live node components + changedFrequent &= ~mask; + } + } + + @Override + void handleFrequencyChange(int bit) { + if (bit == ShaderAttributeSet.ALLOW_ATTRIBUTES_WRITE) { + setFrequencyChangeMask(bit, 0x1); + } + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeValue.java b/src/javax/media/j3d/ShaderAttributeValue.java new file mode 100644 index 0000000..2d2e085 --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeValue.java @@ -0,0 +1,116 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderAttributeValue object encapsulates a uniform shader + * attribute whose value is specified explicitly. The shader variable + * attrName is explicitly set to the specified + * value during rendering. attrName must be + * the name of a valid uniform attribute in the shader in which it is + * used. Otherwise, the attribute name will be ignored and a runtime + * error may be generated. The value must be an instance + * of one of the allowed classes. The allowed classes are: + * Integer, Float, + * Tuple{2,3,4}{i,f}, Matrix{3,4}f. A + * ClassCastException will be thrown if a specified value + * object is not one of the allowed types. Further, the type of the + * value is immutable once a ShaderAttributeValue is constructed. + * Subsequent setValue operations must be called with an object of the + * same type as the one that was used to construct the + * ShaderAttributeValue. Finally, the type of the value + * object must match the type of the corresponding + * attrName variable in the shader in which it is + * used. Otherwise, the shader will not be able to use the attribute + * and a runtime error may be generated. + * + * @see ShaderAttributeSet + * @see ShaderProgram + * + * @since Java 3D 1.4 + */ + +public class ShaderAttributeValue extends ShaderAttributeObject { + /** + * Constructs a new ShaderAttributeValue object with the specified + * (attrName, value) pair. + * A copy of the object is stored. + * + * @param attrName the name of the shader attribute + * @param value the value of the shader attribute + * + * @exception NullPointerException if attrName or value is null + * + * @exception ClassCastException if value is not an instance of + * one of the allowed classes + */ + public ShaderAttributeValue(String attrName, Object value) { + super(attrName, value); + } + + // Implement abstract getValue method + @Override + public Object getValue() { + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_VALUE_READ)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject0")); + + return ((ShaderAttributeValueRetained)this.retained).getValue(); + } + + // Implement abstract setValue method + @Override + public void setValue(Object value) { + + if (value == null) { + throw new NullPointerException(); + } + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_VALUE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject1")); + + if (isLive()) + ((ShaderAttributeValueRetained)this.retained).setValue(value); + else + ((ShaderAttributeValueRetained)this.retained).initValue(value); + + } + + /** + * Creates a retained mode ShaderAttributeValueRetained object that this + * ShaderAttributeValue component object will point to. + */ + @Override + void createRetained() { + this.retained = new ShaderAttributeValueRetained(); + this.retained.setSource(this); + } + +} diff --git a/src/javax/media/j3d/ShaderAttributeValueRetained.java b/src/javax/media/j3d/ShaderAttributeValueRetained.java new file mode 100644 index 0000000..9555355 --- /dev/null +++ b/src/javax/media/j3d/ShaderAttributeValueRetained.java @@ -0,0 +1,537 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4f; +import javax.vecmath.Point2f; +import javax.vecmath.Point2i; +import javax.vecmath.Point3f; +import javax.vecmath.Point3i; +import javax.vecmath.Point4f; +import javax.vecmath.Point4i; +import javax.vecmath.Tuple2f; +import javax.vecmath.Tuple2i; +import javax.vecmath.Tuple3f; +import javax.vecmath.Tuple3i; +import javax.vecmath.Tuple4f; +import javax.vecmath.Tuple4i; + +/** + * The ShaderAttributeValueRetained object encapsulates a uniform shader + * attribute whose value is specified explicitly. + */ + +class ShaderAttributeValueRetained extends ShaderAttributeObjectRetained { + + ShaderAttributeValueRetained() { + } + + @Override + synchronized void createMirrorObject() { + // System.err.println("ShaderAttributeValueRetained : createMirrorObject"); + // This method should only call by setLive(). + if (mirror == null) { + ShaderAttributeValueRetained mirrorSAV = new ShaderAttributeValueRetained(); + mirrorSAV.createObjectData(getValue()); + mirror = mirrorSAV; + mirror.source = source; + + } + initMirrorObject(); + } + + /** + * Computes the base class from the specified object. A + * ClassCastException is thrown if the object is not an instance + * of one of the allowed classes. + */ + @Override + int computeClassType(Object value) { + Class objClass = value.getClass(); + if (objClass.isArray()) { + throw new ClassCastException(objClass + " -- array class not allowed"); + } + + for (int i = 0; i < classTable.length; i++) { + if (classTable[i].isInstance(value)) { + return i; + } + } + throw new ClassCastException(objClass + " -- unrecognized class"); + } + + /** + * Returns the base class represented by the specified class type. + */ + @Override + Class getBaseClass(int classType) { + return classTable[classType]; + } + + /** + * Creates an attribute wrapper object of the specified class + * type, and stores the specified object. + */ + @Override + AttrWrapper createAttrWrapper(Object value, int classType) { + ValueWrapper attrWrapper = null; + switch (classType) { + case TYPE_INTEGER: + attrWrapper = new IntegerWrapper(); + break; + case TYPE_FLOAT: + attrWrapper = new FloatWrapper(); + break; +// case TYPE_DOUBLE: +// attrWrapper = new DoubleWrapper(); +// break; + case TYPE_TUPLE2I: + attrWrapper = new Tuple2iWrapper(); + break; + case TYPE_TUPLE2F: + attrWrapper = new Tuple2fWrapper(); + break; +// case TYPE_TUPLE2D: +// attrWrapper = new Tuple2dWrapper(); +// break; + case TYPE_TUPLE3I: + attrWrapper = new Tuple3iWrapper(); + break; + case TYPE_TUPLE3F: + attrWrapper = new Tuple3fWrapper(); + break; +// case TYPE_TUPLE3D: +// attrWrapper = new Tuple3dWrapper(); +// break; + case TYPE_TUPLE4I: + attrWrapper = new Tuple4iWrapper(); + break; + case TYPE_TUPLE4F: + attrWrapper = new Tuple4fWrapper(); + break; +// case TYPE_TUPLE4D: +// attrWrapper = new Tuple4dWrapper(); +// break; + case TYPE_MATRIX3F: + attrWrapper = new Matrix3fWrapper(); + break; +// case TYPE_MATRIX3D: +// attrWrapper = new Matrix3dWrapper(); +// break; + case TYPE_MATRIX4F: + attrWrapper = new Matrix4fWrapper(); + break; +// case TYPE_MATRIX4D: +// attrWrapper = new Matrix4dWrapper(); +// break; + default: + // Should never get here + assert false; + return null; + } + + attrWrapper.set(value); + return attrWrapper; + } + + // + // The following wrapper classes are used to store a copy of the + // user-specified shader attribute value. There is a wrapper class + // for each supported base class. + // + + // Base wrapper class for non-array attribute types + static abstract class ValueWrapper extends AttrWrapper { + // No additional fields or methods are defined in this class + } + + // Wrapper class for Integer + static class IntegerWrapper extends ValueWrapper { + private int[] value = new int[1]; + + @Override + void set(Object value) { + this.value[0] = ((Integer)value).intValue(); + } + + @Override + Object get() { + return new Integer(this.value[0]); + } + + @Override + Object getRef() { + return this.value; + } + } + + // Wrapper class for Float + static class FloatWrapper extends ValueWrapper { + private float[] value = new float[1]; + + @Override + void set(Object value) { + this.value[0] = ((Float)value).floatValue(); + } + + @Override + Object get() { + return new Float(this.value[0]); + } + + @Override + Object getRef() { + return this.value; + } + } + + /* + // Wrapper class for Double + static class DoubleWrapper extends ValueWrapper { + private double[] value = new double[1]; + + void set(Object value) { + this.value[0] = ((Double)value).doubleValue(); + } + + Object get() { + return new Double(value[0]); + } + + Object getRef() { + return value; + } + } + */ + + // Wrapper class for Tuple2i + static class Tuple2iWrapper extends ValueWrapper { + private int[] value = new int[2]; + + @Override + void set(Object value) { + ((Tuple2i)value).get(this.value); + } + + @Override + Object get() { + return new Point2i(value); + } + + @Override + Object getRef() { + return value; + } + } + + // Wrapper class for Tuple2f + static class Tuple2fWrapper extends ValueWrapper { + private float[] value = new float[2]; + + @Override + void set(Object value) { + ((Tuple2f)value).get(this.value); + } + + @Override + Object get() { + return new Point2f(value); + } + + @Override + Object getRef() { + return value; + } + } + + /* + // Wrapper class for Tuple2d + static class Tuple2dWrapper extends ValueWrapper { + private double[] value = new double[2]; + + void set(Object value) { + ((Tuple2d)value).get(this.value); + } + + Object get() { + return new Point2d(value); + } + + Object getRef() { + return value; + } + } + */ + + // Wrapper class for Tuple3i + static class Tuple3iWrapper extends ValueWrapper { + private int[] value = new int[3]; + + @Override + void set(Object value) { + ((Tuple3i)value).get(this.value); + } + + @Override + Object get() { + return new Point3i(value); + } + + @Override + Object getRef() { + return value; + } + } + + // Wrapper class for Tuple3f + static class Tuple3fWrapper extends ValueWrapper { + private float[] value = new float[3]; + + @Override + void set(Object value) { + ((Tuple3f)value).get(this.value); + } + + @Override + Object get() { + return new Point3f(value); + } + + @Override + Object getRef() { + return value; + } + } + + /* + // Wrapper class for Tuple3d + static class Tuple3dWrapper extends ValueWrapper { + private double[] value = new double[3]; + + void set(Object value) { + ((Tuple3d)value).get(this.value); + } + + Object get() { + return new Point3d(value); + } + + Object getRef() { + return value; + } + } + */ + + // Wrapper class for Tuple4i + static class Tuple4iWrapper extends ValueWrapper { + private int[] value = new int[4]; + + @Override + void set(Object value) { + ((Tuple4i)value).get(this.value); + } + + @Override + Object get() { + return new Point4i(value); + } + + @Override + Object getRef() { + return value; + } + } + + // Wrapper class for Tuple4f + static class Tuple4fWrapper extends ValueWrapper { + private float[] value = new float[4]; + + @Override + void set(Object value) { + ((Tuple4f)value).get(this.value); + } + + @Override + Object get() { + return new Point4f(value); + } + + @Override + Object getRef() { + return value; + } + } + + /* + // Wrapper class for Tuple4d + static class Tuple4dWrapper extends ValueWrapper { + private double[] value = new double[4]; + + void set(Object value) { + ((Tuple4d)value).get(this.value); + } + + Object get() { + return new Point4d(value); + } + + Object getRef() { + return value; + } + } + */ + + // Wrapper class for Matrix3f + static class Matrix3fWrapper extends ValueWrapper { + private float[] value = new float[9]; + + @Override + void set(Object value) { + Matrix3f m = (Matrix3f)value; + this.value[0] = m.m00; + this.value[1] = m.m01; + this.value[2] = m.m02; + this.value[3] = m.m10; + this.value[4] = m.m11; + this.value[5] = m.m12; + this.value[6] = m.m20; + this.value[7] = m.m21; + this.value[8] = m.m22; + } + + @Override + Object get() { + return new Matrix3f(value); + } + + @Override + Object getRef() { + return value; + } + } + + /* + // Wrapper class for Matrix3d + static class Matrix3dWrapper extends ValueWrapper { + private double[] value = new double[9]; + + void set(Object value) { + Matrix3d m = (Matrix3d)value; + this.value[0] = m.m00; + this.value[1] = m.m01; + this.value[2] = m.m02; + this.value[3] = m.m10; + this.value[4] = m.m11; + this.value[5] = m.m12; + this.value[6] = m.m20; + this.value[7] = m.m21; + this.value[8] = m.m22; + } + + Object get() { + return new Matrix3d(value); + } + + Object getRef() { + return value; + } + } + */ + + // Wrapper class for Matrix4f + static class Matrix4fWrapper extends ValueWrapper { + private float[] value = new float[16]; + + @Override + void set(Object value) { + Matrix4f m = (Matrix4f)value; + this.value[0] = m.m00; + this.value[1] = m.m01; + this.value[2] = m.m02; + this.value[3] = m.m03; + this.value[4] = m.m10; + this.value[5] = m.m11; + this.value[6] = m.m12; + this.value[7] = m.m13; + this.value[8] = m.m20; + this.value[9] = m.m21; + this.value[10] = m.m22; + this.value[11] = m.m23; + this.value[12] = m.m30; + this.value[13] = m.m31; + this.value[14] = m.m32; + this.value[15] = m.m33; + } + + @Override + Object get() { + return new Matrix4f(value); + } + + @Override + Object getRef() { + return value; + } + } + + /* + // Wrapper class for Matrix4d + static class Matrix4dWrapper extends ValueWrapper { + private double[] value = new double[16]; + + void set(Object value) { + Matrix4d m = (Matrix4d)value; + this.value[0] = m.m00; + this.value[1] = m.m01; + this.value[2] = m.m02; + this.value[3] = m.m03; + this.value[4] = m.m10; + this.value[5] = m.m11; + this.value[6] = m.m12; + this.value[7] = m.m13; + this.value[8] = m.m20; + this.value[9] = m.m21; + this.value[10] = m.m22; + this.value[11] = m.m23; + this.value[12] = m.m30; + this.value[13] = m.m31; + this.value[14] = m.m32; + this.value[15] = m.m33; + } + + Object get() { + return new Matrix4d(value); + } + + Object getRef() { + return value; + } + } + */ + +} diff --git a/src/javax/media/j3d/ShaderBin.java b/src/javax/media/j3d/ShaderBin.java new file mode 100644 index 0000000..520e64c --- /dev/null +++ b/src/javax/media/j3d/ShaderBin.java @@ -0,0 +1,367 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Map; + + +// XXXX : We should have a common Bin object that all other Bins extend from. + + +//class ShaderBin extends Object implements ObjectUpdate, NodeComponentUpdate { +class ShaderBin implements ObjectUpdate { + + /** + * Node component dirty mask. + */ + static final int SHADER_PROGRAM_DIRTY = 0x1; + static final int SHADER_ATTRIBUTE_SET_DIRTY = 0x2; + + + /** + * The RenderBin for this object + */ + RenderBin renderBin = null; + + /** + * The AttributeBin that this ShaderBin resides + */ + AttributeBin attributeBin = null; + + /** + * The references to the next and previous ShaderBins in the + * list. + */ + ShaderBin next = null; + ShaderBin prev = null; + + /** + * The list of TextureBins in this ShaderBin + */ + TextureBin textureBinList = null; + +/** + * The list of TextureBins to be added for the next frame + */ +ArrayList addTextureBins = new ArrayList(); + + boolean onUpdateList = false; + + int numEditingTextureBins = 0; + + int componentDirty = 0; + ShaderAppearanceRetained shaderAppearance = null; + ShaderProgramRetained shaderProgram = null; + ShaderAttributeSetRetained shaderAttributeSet = new ShaderAttributeSetRetained(); + + ShaderBin(ShaderAppearanceRetained sApp, RenderBin rBin) { + reset(sApp, rBin); + } + + void reset(ShaderAppearanceRetained sApp, RenderBin rBin) { + prev = null; + next = null; + renderBin = rBin; + attributeBin = null; + textureBinList = null; + onUpdateList = false; + numEditingTextureBins = 0; + addTextureBins.clear(); + if(sApp != null) { + shaderProgram = sApp.shaderProgram; + shaderAttributeSet = sApp.shaderAttributeSet; + } + else { + shaderProgram = null; + shaderAttributeSet = null; + } + shaderAppearance = sApp; + } + + void clear() { + reset(null, null); + } + + /** + * This tests if the qiven ra.shaderProgram match this shaderProgram + */ + boolean equals(ShaderAppearanceRetained sApp) { + + ShaderProgramRetained sp; + ShaderAttributeSetRetained ss; + + if (sApp == null) { + sp = null; + ss = null; + } else { + sp = sApp.shaderProgram; + ss = sApp.shaderAttributeSet; + } + + if((shaderProgram != sp) || (shaderAttributeSet != ss)) { + return false; + } + + return true; + + } + + @Override + public void updateObject() { + TextureBin t; + int i; + + if (addTextureBins.size() > 0) { + t = addTextureBins.get(0); + if (textureBinList == null) { + textureBinList = t; + + } + else { + // Look for a TextureBin that has the same texture + insertTextureBin(t); + } + for (i = 1; i < addTextureBins.size() ; i++) { + t = addTextureBins.get(i); + // Look for a TextureBin that has the same texture + insertTextureBin(t); + + } + } + addTextureBins.clear(); + onUpdateList = false; + + } + + void insertTextureBin(TextureBin t) { + TextureBin tb; + 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) { + + t.environmentSet = this.attributeBin.environmentSet; + t.attributeBin = this.attributeBin; + t.shaderBin = this; + + attributeBin.updateFromShaderBin(ra); + addTextureBins.add(t); + + if (!onUpdateList) { + rb.objUpdateList.add(this); + onUpdateList = true; + } + } + + /** + * Removes the given TextureBin from this ShaderBin. + */ + void removeTextureBin(TextureBin t) { + + // If the TextureBin being remove is contained in addTextureBins, + // then remove the TextureBin from the addList + if (addTextureBins.contains(t)) { + addTextureBins.remove(addTextureBins.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.shaderBin = null; + t.prev = null; + t.next = null; + + t.clear(); + + if (textureBinList == null && addTextureBins.size() == 0 ) { + // Note: Removal of this shaderBin as a user of the rendering + // atttrs is done during removeRenderAtom() in RenderMolecule.java + attributeBin.removeShaderBin(this); + } + } + + /** + * Renders this ShaderBin + */ + void render(Canvas3D cv) { + + TextureBin tb; + + // System.err.println("ShaderBin.render() shaderProgram = " + shaderProgram); + + // include this ShaderBin to the to-be-updated list in canvas + cv.setStateToUpdate(Canvas3D.SHADERBIN_BIT, this); + + tb = textureBinList; + while (tb != null) { + tb.render(cv); + tb = tb.next; + } + } + + void updateAttributes(Canvas3D cv) { + + // System.err.println("ShaderBin.updateAttributes() shaderProgram is " + shaderProgram); + if (shaderProgram != null) { + // Compile, link, and enable shader program + shaderProgram.updateNative(cv, true); + + if (shaderAttributeSet != null) { + shaderAttributeSet.updateNative(cv, shaderProgram); + } + + } + else { + if (cv.shaderProgram != null) { + // Disable shader program + cv.shaderProgram.updateNative(cv, false); + } + } + + cv.shaderBin = this; + cv.shaderProgram = shaderProgram; + } + + void updateNodeComponent() { + // System.err.println("ShaderBin.updateNodeComponent() ..."); + + // We don't need to clone shaderProgram. + // ShaderProgram object can't be modified once it is live, + // so each update should be a new reference. + if ((componentDirty & SHADER_PROGRAM_DIRTY) != 0) { + // System.err.println(" - SHADER_PROGRAM_DIRTY"); + + shaderProgram = shaderAppearance.shaderProgram; + } + + // We need to clone the shaderAttributeSet. + if ((componentDirty & SHADER_ATTRIBUTE_SET_DIRTY) != 0) { + // System.err.println(" - SHADER_ATTRIBUTE_SET_DIRTY"); + + Map attrs = shaderAttributeSet.getAttrs(); + attrs.clear(); + if(shaderAppearance.shaderAttributeSet != null) { + attrs.putAll(shaderAppearance.shaderAttributeSet.getAttrs()); + } + } + + componentDirty = 0; + } + + void incrActiveTextureBin() { + numEditingTextureBins++; + } + + void decrActiveTextureBin() { + numEditingTextureBins--; + } +} diff --git a/src/javax/media/j3d/ShaderConstants.java b/src/javax/media/j3d/ShaderConstants.java new file mode 100644 index 0000000..4a090c7 --- /dev/null +++ b/src/javax/media/j3d/ShaderConstants.java @@ -0,0 +1,48 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The ShaderConstants class contains internal constants used by other + * Shader classes. + */ +class ShaderConstants extends Object { + + // + // The following bits are used in the messages for various Shader objects. + // + + // ShaderAttributeSet bits -- indicates which attribute + // operation in this ShaderAttributeSet object is needed. + static final int ATTRIBUTE_SET_PUT = 0x0001; + static final int ATTRIBUTE_SET_REMOVE = 0x0002; + static final int ATTRIBUTE_SET_CLEAR = 0x0004; + + // ShaderAttribute bits + static final int ATTRIBUTE_VALUE_UPDATE = 0x0008; + +} diff --git a/src/javax/media/j3d/ShaderError.java b/src/javax/media/j3d/ShaderError.java new file mode 100644 index 0000000..705aacc --- /dev/null +++ b/src/javax/media/j3d/ShaderError.java @@ -0,0 +1,425 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.io.PrintStream; + +/** + * ShaderError is a container object that holds the details of + * a runtime error that occurs while compiling or executing a + * programmable shader. + * + * @since Java 3D 1.4 + */ +public class ShaderError extends Object { + private int errorCode = NO_ERROR; + private String errorMessage = null; + private String detailMessage = null; + private Canvas3D canvas = null; + private Shape3D shape = null; + private Geometry geometry = null; + private ShaderAppearance shaderApp = null; + private ShaderProgram shaderProgram = null; + private Shader shader = null; + private ShaderAttributeSet shaderAttributeSet = null; + private ShaderAttribute shaderAttribute = null; + + /** + * Indicates that no error occurred. + */ + public static final int NO_ERROR = 0; + + /** + * Indicates that an error occurred while compiling a shader. + */ + public static final int COMPILE_ERROR = 1; + + /** + * Indicates that an error occurred while linking a shader. + */ + public static final int LINK_ERROR = 2; + + /** + * Indicates a error in looking up a vertex attribute + * name within a given shader program. + */ + public static final int VERTEX_ATTRIBUTE_LOOKUP_ERROR = 3; + + /** + * Indicates a error in looking up the location of a uniform + * shader attribute name within a given shader program. + */ + public static final int SHADER_ATTRIBUTE_LOOKUP_ERROR = 4; + + /** + * Indicates a error caused by a ShaderAttribute whose name does not + * appear in the list of shader attribute names in the corresponding + * ShaderProgram object. + */ + public static final int SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR = 5; + + /** + * Indicates a error in the type of the attribute versus what the shader + * program was expecting. + */ + public static final int SHADER_ATTRIBUTE_TYPE_ERROR = 6; + + /** + * Indicates that the specified shading language is not supported + * on the screen display device. + */ + public static final int UNSUPPORTED_LANGUAGE_ERROR = 7; + + + /** + * Constructs a new ShaderError object indicating no error. The + * error code is set to NO_ERROR. All other fields + * are initialized to null, including the error message. + */ + public ShaderError() { + } + + /** + * Constructs a new ShaderError object with the given error code + * and message. All other fields are initialized to null. + * + * @param errorCode the error code for this shader error. + * + * @param errorMessage a short error message describing this + * shader error. + */ + public ShaderError(int errorCode, String errorMessage) { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + /** + * Prints a verbose error report to System.err. This verbose + * output includes the error code, error message, detail message, + * and all relevant Java 3D objects. + */ + public void printVerbose() { + printVerbose(System.err); + } + + /** + * Prints a verbose error report to the specified PrintStream. + * This verbose output includes the error code, error message, + * detail message, and all relevant Java 3D objects. + * + * @param printStream the print stream on which to print the error + * report. + */ + public void printVerbose(PrintStream printStream) { + printStream.println(this); + if (canvas != null) { + printStream.println("canvas = " + canvas); + } + if (shape != null) { + printStream.println("shape = " + shape); + } + if (geometry != null) { + printStream.println("geometry = " + geometry); + } + if (shaderApp != null) { + printStream.println("shaderApp = " + shaderApp); + } + if (shaderProgram != null) { + printStream.println("shaderProgram = " + shaderProgram); + } + if (shader != null) { + printStream.println("shader = " + shader); + } + if (shaderAttributeSet != null) { + printStream.println("shaderAttributeSet = " + shaderAttributeSet); + } + if (shaderAttribute != null) { + printStream.println("shaderAttribute = " + shaderAttribute); + } + + if (detailMessage != null) { + printStream.println(); + printStream.println("Detail Message"); + printStream.println("--------------"); + printStream.println(detailMessage); + } + } + + /** + * Sets the error code for this shader error. This represents the + * type of error that occurred. + * + * @param errorCode the error code for this shader error. + */ + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + /** + * Returns the error code for this shader error. + * + * @return the error code. + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Sets the error message for this shader error. This is a short + * message describing the error, and is included as part of + * toString(). + * + * @param errorMessage a short error message describing this + * shader error. + */ + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + /** + * Returns the error message for this shader error. + * + * @return a short error message describing this shader error. + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Sets the detail message for this shader error. This is a + * detailed error message, typically produced by the shader + * compiler, and is not included as part of toString(). + * + * @param detailMessage a detailed message describing this shader + * error in more detail. + */ + public void setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + } + + /** + * Returns the detail message for this shader error. + * + * @return the detail message for this shader error. + */ + public String getDetailMessage() { + return detailMessage; + } + + /** + * Sets the canvas associated with this shader error. + * + * @param canvas the canvas associated with this shader error. + */ + public void setCanvas3D(Canvas3D canvas) { + this.canvas = canvas; + } + + /** + * Returns the canvas associated with this shader error. + * + * @return the canvas associated with this shader error. + */ + public Canvas3D getCanvas3D() { + return this.canvas; + } + + /** + * Sets the shape node associated with this shader error. + * + * @param shape the shape node associated with this shader error. + */ + public void setShape3D(Shape3D shape) { + this.shape = shape; + } + + /** + * Returns the shape node associated with this shader error. + * + * @return the shape node associated with this shader error. + */ + public Shape3D getShape3D() { + return this.shape; + } + + /** + * Sets the geometry associated with this shader error. + * + * @param geometry the geometry associated with this shader error. + */ + public void setGeometry(Geometry geometry) { + this.geometry = geometry; + } + + /** + * Returns the geometry associated with this shader error. + * + * @return the geometry associated with this shader error. + */ + public Geometry getGeometry() { + return this.geometry; + } + + /** + * Sets the shader appearance associated with this shader error. + * + * @param shaderApp the shader appearance associated with this shader error. + */ + public void setShaderAppearance(ShaderAppearance shaderApp) { + this.shaderApp = shaderApp; + } + + /** + * Returns the shader appearance associated with this shader error. + * + * @return the shader appearance associated with this shader error. + */ + public ShaderAppearance getShaderAppearance() { + return this.shaderApp; + } + + /** + * Sets the shader program associated with this shader error. + * + * @param shaderProgram the shader program associated with this shader error. + */ + public void setShaderProgram(ShaderProgram shaderProgram) { + this.shaderProgram = shaderProgram; + } + + /** + * Returns the shader program associated with this shader error. + * + * @return the shader program associated with this shader error. + */ + public ShaderProgram getShaderProgram() { + return this.shaderProgram; + } + + /** + * Sets the shader object associated with this shader error. + * + * @param shader the shader object associated with this shader error. + */ + public void setShader(Shader shader) { + this.shader = shader; + } + + /** + * Returns the shader object associated with this shader error. + * + * @return the shader object associated with this shader error. + */ + public Shader getShader() { + return this.shader; + } + + /** + * Sets the shader attribute set associated with this shader error. + * + * @param shaderAttributeSet the shader attribute set associated with this shader error. + */ + public void setShaderAttributeSet(ShaderAttributeSet shaderAttributeSet) { + this.shaderAttributeSet = shaderAttributeSet; + } + + /** + * Returns the shader attribute set associated with this shader error. + * + * @return the shader attribute set associated with this shader error. + */ + public ShaderAttributeSet getShaderAttributeSet() { + return this.shaderAttributeSet; + } + + /** + * Sets the shader attribute associated with this shader error. + * + * @param shaderAttribute the shader attribute associated with this shader error. + */ + public void setShaderAttribute(ShaderAttribute shaderAttribute) { + this.shaderAttribute = shaderAttribute; + } + + /** + * Returns the shader attribute associated with this shader error. + * + * @return the shader attribute associated with this shader error. + */ + public ShaderAttribute getShaderAttribute() { + return this.shaderAttribute; + } + + + /** + * Returns a short string that describes this shader error. The + * string is composed of the textual description of the errorCode, + * a ": ", and the errorMessage field. If the errorMessage is + * null then the ": " and the errorMessage are omitted. + * + * @return a string representation of this shader error. + */ + @Override + public String toString() { + // Concatenate string representation of error code with error message + String errorCodeStr; + switch (errorCode) { + case NO_ERROR: + errorCodeStr = "NO_ERROR"; + break; + case COMPILE_ERROR: + errorCodeStr = "COMPILE_ERROR"; + break; + case LINK_ERROR: + errorCodeStr = "LINK_ERROR"; + break; + case VERTEX_ATTRIBUTE_LOOKUP_ERROR: + errorCodeStr = "VERTEX_ATTRIBUTE_LOOKUP_ERROR"; + break; + case SHADER_ATTRIBUTE_LOOKUP_ERROR: + errorCodeStr = "SHADER_ATTRIBUTE_LOOKUP_ERROR"; + break; + case SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR: + errorCodeStr = "SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR"; + break; + case SHADER_ATTRIBUTE_TYPE_ERROR: + errorCodeStr = "SHADER_ATTRIBUTE_TYPE_ERROR"; + break; + case UNSUPPORTED_LANGUAGE_ERROR: + errorCodeStr = "UNSUPPORTED_LANGUAGE_ERROR"; + break; + default: + errorCodeStr = "UNKNOWN ERROR CODE (" + errorCode + ")"; + } + + if (errorMessage == null) { + return errorCodeStr; + } + + return errorCodeStr + ": " + errorMessage; + } +} diff --git a/src/javax/media/j3d/ShaderErrorListener.java b/src/javax/media/j3d/ShaderErrorListener.java new file mode 100644 index 0000000..0e02e8f --- /dev/null +++ b/src/javax/media/j3d/ShaderErrorListener.java @@ -0,0 +1,47 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Listener interface for monitoring errors in Shader Programs. + * Compile and link errors are reported by the shader compiler, as are + * runtime errors, such as those resulting from shader attributes that + * aren't found or are of the wrong type. + * + * @see VirtualUniverse#addShaderErrorListener + * + * @since Java 3D 1.4 + */ +public interface ShaderErrorListener { + /** + * Invoked when an error occurs while compiling, linking or + * executing a programmable shader. + * + * @param error object that contains the details of the error. + */ + public void errorOccurred(ShaderError error); +} diff --git a/src/javax/media/j3d/ShaderId.java b/src/javax/media/j3d/ShaderId.java new file mode 100644 index 0000000..eb50c35 --- /dev/null +++ b/src/javax/media/j3d/ShaderId.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Tagging interface for shader objects. The rendering pipelines + * will define concrete classes that implement this interface. All code that + * uses the tagged objects will be in the pipelines. + */ +interface ShaderId { + // No methods or constants defined at this time +} diff --git a/src/javax/media/j3d/ShaderProgram.java b/src/javax/media/j3d/ShaderProgram.java new file mode 100644 index 0000000..46499a1 --- /dev/null +++ b/src/javax/media/j3d/ShaderProgram.java @@ -0,0 +1,209 @@ +/* + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The ShaderProgram node component object is the abstract base class + * for programmable shader programs. Each concrete instance of a + * ShaderProgram is a container for a set of Shader objects. The set + * of Shaders contained in the ShaderProgram is a complete program for + * the Graphics Pipeline Unit (GPU) of the graphics accelerator. It is + * specified using the shader language defined by the + * ShaderProgram. The currently defined shader languages are: Cg and + * GLSL. + * + *

+ * NOTE: Applications should not extend this class. + * + * @see Shader + * @see ShaderAppearance#setShaderProgram + * + * @since Java 3D 1.4 + */ + +public abstract class ShaderProgram extends NodeComponent { + + /** + * Specifies that this ShaderProgram object allows reading + * its shaders. + */ + public static final int ALLOW_SHADERS_READ = + CapabilityBits.SHADER_PROGRAM_ALLOW_SHADERS_READ; + + /** + * Specifies that this ShaderProgram object allows reading + * its shader or vertex attribute names. + */ + public static final int ALLOW_NAMES_READ = + CapabilityBits.SHADER_PROGRAM_ALLOW_NAMES_READ; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SHADERS_READ, + ALLOW_NAMES_READ + }; + + /* + * Default values (copied from GeometryArray.java): + * + * vertexAttrNames : null
+ */ + + /** + * Package scope constructor so it can't be subclassed by classes + * outside the javax.media.j3d package. + */ + ShaderProgram() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Sets the vertex attribute names array for this ShaderProgram + * object. Each element in the array specifies the shader + * attribute name that is bound to the corresponding numbered + * vertex attribute within a GeometryArray object that uses this + * shader program. Array element 0 specifies the name of + * GeometryArray vertex attribute 0, array element 1 specifies the + * name of GeometryArray vertex attribute 1, and so forth. + * The array of names may be null or empty (0 length), but the + * elements of the array must be non-null. + * + * @param vertexAttrNames array of vertex attribute names for this + * shader program. A copy of this array is made. + * + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + * + * @exception NullPointerException if any element in the + * vertexAttrNames array is null. + */ + public abstract void setVertexAttrNames(String[] vertexAttrNames); + + /** + * Retrieves the vertex attribute names array from this + * ShaderProgram object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @return a copy of this ShaderProgram's array of vertex attribute names. + */ + public abstract String[] getVertexAttrNames(); + + + /** + * Sets the shader attribute names array for this ShaderProgram + * object. Each element in the array specifies a shader + * attribute name that may be set via a ShaderAttribute object. + * Only those attributes whose names that appear in the shader + * attribute names array can be set for a given shader program. + * The array of names may be null or empty (0 length), but the + * elements of the array must be non-null. + * + *

+ * TODO: finish this. + * + * @param shaderAttrNames array of shader attribute names for this + * shader program. A copy of this array is made. + * + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + * + * @exception NullPointerException if any element in the + * shaderAttrNames array is null. + */ + public abstract void setShaderAttrNames(String[] shaderAttrNames); + + /** + * Retrieves the shader attribute names array from this + * ShaderProgram object. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @return a copy of this ShaderProgram's array of shader attribute names. + */ + public abstract String[] getShaderAttrNames(); + + + /** + * Copies the specified array of shaders into this shader + * program. This method makes a shallow copy of the array. The + * array of shaders may be null or empty (0 length), but the + * elements of the array must be non-null. The shading + * language of each shader in the array must match the + * subclass. Subclasses may impose additional restrictions. + * + * @param shaders array of Shader objects to be copied into this + * ShaderProgram + * + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + * + * @exception IllegalArgumentException if the shading language of + * any shader in the shaders array doesn't match the type of the + * subclass. + * + * @exception NullPointerException if any element in the + * shaders array is null. + */ + public abstract void setShaders(Shader[] shaders); + + /** + * Retrieves the array of shaders from this shader program. A + * shallow copy of the array is returned. The return value may + * be null. + * + * @return a copy of this ShaderProgram's array of Shader objects + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + public abstract Shader[] getShaders(); + + + // Default shader error listener class + private static ShaderErrorListener defaultErrorListener = null; + + synchronized static ShaderErrorListener getDefaultErrorListener() { + if (defaultErrorListener == null) { + defaultErrorListener = new DefaultErrorListener(); + } + + return defaultErrorListener; + } + + static class DefaultErrorListener implements ShaderErrorListener { + @Override + public void errorOccurred(ShaderError error) { + System.err.println(); + System.err.println("DefaultShaderErrorListener.errorOccurred:"); + error.printVerbose(); + } + } +} diff --git a/src/javax/media/j3d/ShaderProgramId.java b/src/javax/media/j3d/ShaderProgramId.java new file mode 100644 index 0000000..3f22fb3 --- /dev/null +++ b/src/javax/media/j3d/ShaderProgramId.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * Tagging interface for shader program objects. The rendering pipelines + * will define concrete classes that implement this interface. All code that + * uses the tagged objects will be in the pipelines. + */ +interface ShaderProgramId { + // No methods or constants defined at this time +} diff --git a/src/javax/media/j3d/ShaderProgramRetained.java b/src/javax/media/j3d/ShaderProgramRetained.java new file mode 100644 index 0000000..ec63b3a --- /dev/null +++ b/src/javax/media/j3d/ShaderProgramRetained.java @@ -0,0 +1,1233 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +/** + * The ShaderProgramRetained object is a component object of an AppearanceRetained + * object that defines the shader properties used when programmable shader is + * enabled. ShaderProgramRetained object is an abstract class. All shader program + * objects must be created as either a GLSLShaderProgramRetained object or a + * CgShaderProgramRetained object. + */ +abstract class ShaderProgramRetained extends NodeComponentRetained { + + // Each element in the array corresponds to a unique renderer if shared + // context or a unique canvas otherwise. + protected ShaderProgramData shaderProgramData[]; + + // Flag indicating whether an UNSUPPORTED_LANGUAGE_ERROR has + // already been reported for this shader program object. It is + // set in verifyShaderProgram and cleared in setLive or clearLive. + // TODO KCR: Add code to clear this in setLive or clearLive + private boolean unsupportedErrorReported = false; + + // Flag indicating whether a LINK_ERROR has occurred for this shader program + // object. It is set in updateNative to indicate that the linkShaderProgram + // operation failed. It is cleared in setLive or clearLive. + // TODO KCR: Add code to clear this in setLive or clearLive + private boolean linkErrorOccurred = false; + + // an array of shaders used by this shader program + protected ShaderRetained[] shaders; + + // an array of vertex attribute names + protected String[] vertexAttrNames; + + // an array of (uniform) shader attribute names + protected String[] shaderAttrNames; + +// Set of ShaderAttribute objects for which we have already reported an error +private HashSet shaderAttrErrorSet = null; + + // need to synchronize access from multiple rendering threads + Object resourceLock = new Object(); + + // Package-scope default constructor + ShaderProgramRetained() { + } + + /** + * Sets the vertex attribute names array for this ShaderProgram + * object. Each element in the array specifies the shader + * attribute name that is bound to the corresponding numbered + * vertex attribute within a GeometryArray object that uses this + * shader program. Array element 0 specifies the name of + * GeometryArray vertex attribute 0, array element 1 specifies the + * name of GeometryArray vertex attribute 1, and so forth. + * The array of names may be null or empty (0 length), but the + * elements of the array must be non-null. + * + * @param vertexAttrNames array of vertex attribute names for this + * shader program. A copy of this array is made. + */ + void setVertexAttrNames(String[] vertexAttrNames) { + if (vertexAttrNames == null) { + this.vertexAttrNames = null; + } + else { + this.vertexAttrNames = vertexAttrNames.clone(); + } + } + + + /** + * Retrieves the vertex attribute names array from this + * ShaderProgram object. + * + * @return a copy of this ShaderProgram's array of vertex attribute names. + */ + String[] getVertexAttrNames() { + + if (vertexAttrNames == null) { + return null; + } + + return vertexAttrNames.clone(); + + } + + + /** + * Sets the shader attribute names array for this ShaderProgram + * object. Each element in the array specifies a shader + * attribute name that may be set via a ShaderAttribute object. + * Only those attributes whose names that appear in the shader + * attribute names array can be set for a given shader program. + * The array of names may be null or empty (0 length), but the + * elements of the array must be non-null. + * + * @param shaderAttrNames array of shader attribute names for this + * shader program. A copy of this array is made. + */ + void setShaderAttrNames(String[] shaderAttrNames) { + if (shaderAttrNames == null) { + this.shaderAttrNames = null; + } + else { + this.shaderAttrNames = shaderAttrNames.clone(); + } + } + + + /** + * Retrieves the shader attribute names array from this + * ShaderProgram object. + * + * @return a copy of this ShaderProgram's array of shader attribute names. + */ + + String[] getShaderAttrNames() { + + if (shaderAttrNames == null) { + return null; + } + + return shaderAttrNames.clone(); + + } + + + + /** + * Copies the specified array of shaders into this shader + * program. This method makes a shallow copy of the array. The + * array of shaders may be null or empty (0 length), but the + * elements of the array must be non-null. The shading + * language of each shader in the array must match the + * subclass. Subclasses may impose additional restrictions. + * + * @param shaders array of Shader objects to be copied into this + * ShaderProgram + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalArgumentException if the shading language of + * any shader in the shaders array doesn't match the type of the + * subclass. + */ + void setShaders(Shader[] shaders) { + + if (shaders == null) { + this.shaders = null; + return; + } + + this.shaders = new ShaderRetained[shaders.length]; + + // Copy vertex and fragment shader + for (int i = 0; i < shaders.length; i++) { + this.shaders[i] = (ShaderRetained)shaders[i].retained; + } + + } + + /** + * Retrieves the array of shaders from this shader program. A + * shallow copy of the array is returned. The return value may + * be null. + * + * @return a copy of this ShaderProgram's array of Shader objects + * + */ + Shader[] getShaders() { + + if (shaders == null) { + return null; + } else { + Shader shads[] = + new Shader[shaders.length]; + for (int i = 0; i < shaders.length; i++) { + if (shaders[i] != null) { + shads[i] = (Shader) shaders[i].source; + } else { + shads[i] = null; + } + } + return shads; + } + } + + /** + * Method to create the native shader. + */ + abstract ShaderError createShader(Context ctx, ShaderRetained shader, ShaderId[] shaderIdArr); + + /** + * Method to destroy the native shader. + */ + abstract ShaderError destroyShader(Context ctx, ShaderId shaderId); + + /** + * Method to compile the native shader. + */ + abstract ShaderError compileShader(Context ctx, ShaderId shaderId, String source); + + /** + * Method to create the native shader program. + */ + abstract ShaderError createShaderProgram(Context ctx, ShaderProgramId[] shaderProgramIdArr); + + /** + * Method to destroy the native shader program. + */ + abstract ShaderError destroyShaderProgram(Context ctx, ShaderProgramId shaderProgramId); + + /** + * Method to link the native shader program. + */ + abstract ShaderError linkShaderProgram(Context ctx, ShaderProgramId shaderProgramId, ShaderId[] shaderIds); + + /** + * Method to bind a vertex attribute name to the specified index. + */ + abstract ShaderError bindVertexAttrName(Context ctx, ShaderProgramId shaderProgramId, String attrName, int attrIndex); + + /** + * Method to lookup a list of (uniform) shader attribute names and return + * information about the attributes. + */ + abstract void lookupShaderAttrNames(Context ctx, ShaderProgramId shaderProgramId, String[] attrNames, AttrNameInfo[] attrNameInfoArr); + + /* + * Method to lookup a list of vertex attribute names. + */ + abstract void lookupVertexAttrNames(Context ctx, ShaderProgramId shaderProgramId, String[] attrNames, boolean[] errArr); + + /** + * Method to use the native shader program. + */ + abstract ShaderError enableShaderProgram(Context ctx, ShaderProgramId shaderProgramId); + + /** + * Method to disable the native shader program. + */ + abstract ShaderError disableShaderProgram(Context ctx); + + // ShaderAttributeValue methods + + abstract ShaderError setUniform1i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int value); + + abstract ShaderError setUniform1f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float value); + + abstract ShaderError setUniform2i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value); + + abstract ShaderError setUniform2f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setUniform3i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value); + + abstract ShaderError setUniform3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setUniform4i(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int[] value); + + abstract ShaderError setUniform4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setUniformMatrix3f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + abstract ShaderError setUniformMatrix4f(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + float[] value); + + + // ShaderAttributeArray methods + + abstract ShaderError setUniform1iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setUniform1fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setUniform2iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setUniform2fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setUniform3iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setUniform3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setUniform4iArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + int[] value); + + abstract ShaderError setUniform4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setUniformMatrix3fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + abstract ShaderError setUniformMatrix4fArray(Context ctx, + ShaderProgramId shaderProgramId, + ShaderAttrLoc uniformLocation, + int numElements, + float[] value); + + + /** + * Method to return a flag indicating whether this + * ShaderProgram is supported on the specified Canvas. + */ + abstract boolean isSupported(Canvas3D cv); + + + @Override + void setLive(boolean backgroundGroup, int refCount) { + + // System.err.println("ShaderProgramRetained.setLive()"); + + if (shaders != null) { + for (int i = 0; i < shaders.length; i++){ + shaders[i].setLive(backgroundGroup, refCount); + } + } + + super.doSetLive(backgroundGroup, refCount); + super.markAsLive(); + + } + + @Override + void clearLive(int refCount) { + + // System.err.println("ShaderProgramRetained.clearLive()"); + + super.clearLive(refCount); + + if (shaders != null) { + for (int i = 0; i < shaders.length; i++) { + shaders[i].clearLive(refCount); + } + } + } + + /** + * Method to enable the native shader program. + */ + private ShaderError enableShaderProgram(Canvas3D cv, int cvRdrIndex) { + assert(cvRdrIndex >= 0); + synchronized(resourceLock) { + return enableShaderProgram(cv.ctx, + shaderProgramData[cvRdrIndex].getShaderProgramId()); + } + + } + + /** + * Method to disable the native shader program. + */ + private ShaderError disableShaderProgram(Canvas3D cv) { + return disableShaderProgram(cv.ctx); + } + + /** + * Initializes a mirror object. + */ + @Override + synchronized void initMirrorObject() { + + // Create mirror copy of shaders + if (this.shaders == null) { + ((ShaderProgramRetained)mirror).shaders = null; + } + else { + ((ShaderProgramRetained)mirror).shaders = new ShaderRetained[this.shaders.length]; + // Copy vertex and fragment shader + for (int i = 0; i < this.shaders.length; i++) { + ((ShaderProgramRetained)mirror).shaders[i] = + (ShaderRetained)this.shaders[i].mirror; + } + } + ((ShaderProgramRetained)mirror).shaderProgramData = null; + + // Create mirror copy of vertex attribute names + if (this.vertexAttrNames == null) { + ((ShaderProgramRetained)mirror).vertexAttrNames = null; + } + else { + ((ShaderProgramRetained)mirror).vertexAttrNames = this.vertexAttrNames.clone(); + } + + // Create mirror copy of shader attribute names + if (this.shaderAttrNames == null) { + ((ShaderProgramRetained)mirror).shaderAttrNames = null; + } + else { + ((ShaderProgramRetained)mirror).shaderAttrNames = this.shaderAttrNames.clone(); + } + + // Clear shader attribute error set + ((ShaderProgramRetained)mirror).shaderAttrErrorSet = null; + } + + /** + * Update the "component" field of the mirror object with the given "value" + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + + // ShaderProgram can't be modified once it is live. + assert(false); + System.err.println("ShaderProgramRetained : updateMirrorObject NOT IMPLEMENTED YET"); + } + + /** + * Method to create a ShaderProgramData object for the specified + * canvas/renderer if it doesn't already exist. + * + * Issue 378 : reset the ShaderProgramData object if the context + * has been recreated for the particular canvas / renderer. + */ + private void createShaderProgramData(int cvRdrIndex, long ctxTimeStamp) { + // Create shaderProgram resources if it has not been done. + synchronized(resourceLock) { + if(shaderProgramData == null) { + // We rely on Java to initial the array elements to null. + shaderProgramData = new ShaderProgramData[cvRdrIndex+1]; + } + else if(shaderProgramData.length <= cvRdrIndex) { + // We rely on Java to initial the array elements to null. + ShaderProgramData[] tempSPData = new ShaderProgramData[cvRdrIndex+1]; + System.arraycopy(shaderProgramData, 0, + tempSPData, 0, + shaderProgramData.length); + shaderProgramData = tempSPData; + } + + if(shaderProgramData[cvRdrIndex] == null) { + shaderProgramData[cvRdrIndex] = new ShaderProgramData(); + } else if (shaderProgramData[cvRdrIndex].getCtxTimeStamp() != ctxTimeStamp) { + // Issue 378 - reset the shader program data for this canvas / renderer + // if the context has been recreated + shaderProgramData[cvRdrIndex].reset(); + } + shaderProgramData[cvRdrIndex].setCtxTimeStamp(ctxTimeStamp); + } + } + + /** + * Method to create the native shader program. We must already have + * called createShaderProgramData for this cvRdrIndex. + */ + private ShaderError createShaderProgram(Canvas3D cv, int cvRdrIndex) { + // Create shaderProgram resources if it has not been done. + synchronized(resourceLock) { + assert shaderProgramData[cvRdrIndex].getShaderProgramId() == null; + + ShaderProgramId[] spIdArr = new ShaderProgramId[1]; + ShaderError err = createShaderProgram(cv.ctx, spIdArr); + if(err != null) { + return err; + } + shaderProgramData[cvRdrIndex].setShaderProgramId(spIdArr[0]); + } + + return null; + } + + /** + * Method to link the native shader program. + */ + private ShaderError linkShaderProgram(Canvas3D cv, int cvRdrIndex, + ShaderRetained[] shaders) { + synchronized(resourceLock) { + ShaderId[] shaderIds = new ShaderId[shaders.length]; + for(int i=0; i cvRdrIndex && + shaderProgramData[cvRdrIndex] != null); + +// // Check whether an entry in the shaderProgramData array has been allocated +// if (shaderProgramData == null || +// shaderProgramData.length <= cvRdrIndex || +// shaderProgramData[cvRdrIndex] == null) { +// return; +// } + + ShaderProgramId shaderProgramId = shaderProgramData[cvRdrIndex].getShaderProgramId(); + // Nothing to do if the shaderProgramId is null + if (shaderProgramId == null) { + return; + } + + // Destroy the native resource, set the ID to null for this canvas/renderer, + // and clear the bit in the resourceCreationMask + // Ignore any possible shader error, because there is no meaningful way to report it + destroyShaderProgram(cv.ctx, shaderProgramId); + // Reset this ShaderProgramData object. + shaderProgramData[cvRdrIndex].reset(); + } + } + + + /** + * updateNative is called while traversing the RenderBin to + * update the shader program state + */ + void updateNative(Canvas3D cv, boolean enable) { + // System.err.println("ShaderProgramRetained.updateNative : "); + + final boolean useSharedCtx = cv.useSharedCtx && cv.screen.renderer.sharedCtx != null; + int cvRdrIndex; + long ctxTimeStamp; + + if (useSharedCtx) { + cvRdrIndex = cv.screen.renderer.rendererId; + ctxTimeStamp = cv.screen.renderer.sharedCtxTimeStamp; + } else { + cvRdrIndex = cv.canvasId; + ctxTimeStamp = cv.ctxTimeStamp; + } + + // Create ShaderProgramData object for this canvas/renderer if it doesn't already exist + createShaderProgramData(cvRdrIndex, ctxTimeStamp); + + // Check whether this shader program type is supported for this canvas + if (!verifyShaderProgramSupported(cv)) { + return; + } + + // Just disable shader program and return if enable parameter is set to false + if (!enable) { + // Given the current design, disableShaderProgram cannot return a non-null value, + // so no need to check it + disableShaderProgram(cv); + return; + } + + // Just disable shader program and return if array of shaders is empty, + // or if a previous attempt to link resulted in an error + if (shaders == null || shaders.length == 0 || linkErrorOccurred) { + disableShaderProgram(cv); + return; + } + + boolean loadShaderProgram = false; // flag indicating whether to reload all shaderProgram states + if (getShaderProgramData(cvRdrIndex).getShaderProgramId() == null) { + loadShaderProgram = true; + } + + //System.err.println(".... loadShaderProgram = " + loadShaderProgram); + //System.err.println(".... resourceCreationMask= " + resourceCreationMask); + + ShaderError err = null; + boolean errorOccurred = false; + if (loadShaderProgram) { + if (useSharedCtx) { + // TODO : Need to test useSharedCtx case. ** Untested case ** + cv.makeCtxCurrent(cv.screen.renderer.sharedCtx); + } + + // Create shader resources if not already done + for(int i=0; i < shaders.length; i++) { + // Create ShaderProgramData object for this canvas/renderer if it doesn't already exist + shaders[i].createShaderData(cvRdrIndex, ctxTimeStamp); + + if (shaders[i].compileErrorOccurred) { + errorOccurred = true; + } + else { + err = createShader(cv, cvRdrIndex, shaders[i]); + if (err != null) { + err.setShaderProgram((ShaderProgram)this.source); + err.setShader((Shader)shaders[i].source); + err.setCanvas3D(cv); + notifyErrorListeners(cv, err); + errorOccurred = true; + } + else { + err = compileShader(cv, cvRdrIndex, shaders[i]); + if (err != null) { + err.setShaderProgram((ShaderProgram)this.source); + err.setShader((Shader)shaders[i].source); + err.setCanvas3D(cv); + notifyErrorListeners(cv, err); + destroyShader(cv, cvRdrIndex, shaders[i]); + shaders[i].compileErrorOccurred = true; + errorOccurred = true; + } + } + } + } + + // Create shader program + if (!errorOccurred) { + err = createShaderProgram(cv, cvRdrIndex); + if (err != null) { + err.setShaderProgram((ShaderProgram)this.source); + err.setCanvas3D(cv); + notifyErrorListeners(cv, err); + errorOccurred = true; + } + } + + boolean linked = getShaderProgramData(cvRdrIndex).isLinked(); + if (!linked) { + // Bind vertex attribute names + if (!errorOccurred) { + if (vertexAttrNames != null) { +// System.err.println("vertexAttrNames.length = " + vertexAttrNames.length); + for (int i = 0; i < vertexAttrNames.length; i++) { + err = bindVertexAttrName(cv, cvRdrIndex, vertexAttrNames[i], i); + // Report non-fatal error, if one was detected + if (err != null) { + err.setShaderProgram((ShaderProgram)this.source); + err.setCanvas3D(cv); + notifyErrorListeners(cv, err); + } + } + } + } + + // Link shader program + if (!errorOccurred) { + err = linkShaderProgram(cv, cvRdrIndex, shaders); + if (err != null) { + err.setShaderProgram((ShaderProgram)this.source); + err.setCanvas3D(cv); + notifyErrorListeners(cv, err); + destroyShaderProgram(cv, cvRdrIndex); + linkErrorOccurred = true; + errorOccurred = true; + } + } + + // lookup vertex attribute names + if (!errorOccurred) { + if (vertexAttrNames != null) { + lookupVertexAttrNames(cv, cvRdrIndex, vertexAttrNames); + } + } + + // Lookup shader attribute names + if (!errorOccurred) { + if (shaderAttrNames != null) { +// System.err.println("shaderAttrNames.length = " + shaderAttrNames.length); + lookupShaderAttrNames(cv, cvRdrIndex, shaderAttrNames); + } + } + } + + // Restore current context if we changed it to the shareCtx + if (useSharedCtx) { + cv.makeCtxCurrent(cv.ctx); + } + + // If compilation or link error occured, disable shader program and return + if (errorOccurred) { + disableShaderProgram(cv); + return; + } + } + + // Now we can enable the shader program + enableShaderProgram(cv, cvRdrIndex); + } + + /** + * Update native value for ShaderAttributeValue class + */ + ShaderError setUniformAttrValue(Context ctx, ShaderProgramId shaderProgramId, + ShaderAttrLoc loc, ShaderAttributeValueRetained sav) { + + switch (sav.getClassType()) { + case ShaderAttributeObjectRetained.TYPE_INTEGER: + return setUniform1i(ctx, shaderProgramId, loc, + ((int[])sav.attrWrapper.getRef())[0]); + + case ShaderAttributeObjectRetained.TYPE_FLOAT: + return setUniform1f(ctx, shaderProgramId, loc, + ((float[])sav.attrWrapper.getRef())[0]); + + case ShaderAttributeObjectRetained.TYPE_TUPLE2I: + return setUniform2i(ctx, shaderProgramId, loc, + (int[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE2F: + return setUniform2f(ctx, shaderProgramId, loc, + (float[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE3I: + return setUniform3i(ctx, shaderProgramId, loc, + (int[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE3F: + return setUniform3f(ctx, shaderProgramId, loc, + (float[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE4I: + return setUniform4i(ctx, shaderProgramId, loc, + (int[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE4F: + return setUniform4f(ctx, shaderProgramId, loc, + (float[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_MATRIX3F: + return setUniformMatrix3f(ctx, shaderProgramId, loc, + (float[])sav.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_MATRIX4F: + return setUniformMatrix4f(ctx, shaderProgramId, loc, + (float[])sav.attrWrapper.getRef()); + + default: + // Should never get here + assert false : "Unrecognized ShaderAttributeValue classType"; + return null; + } + } + + /** + * Update native value for ShaderAttributeArray class + */ + ShaderError setUniformAttrArray(Context ctx, ShaderProgramId shaderProgramId, + ShaderAttrLoc loc, ShaderAttributeArrayRetained saa) { + + switch (saa.getClassType()) { + case ShaderAttributeObjectRetained.TYPE_INTEGER: + return setUniform1iArray(ctx, shaderProgramId, loc, saa.length(), + ((int[])saa.attrWrapper.getRef())); + + case ShaderAttributeObjectRetained.TYPE_FLOAT: + return setUniform1fArray(ctx, shaderProgramId, loc, saa.length(), + ((float[])saa.attrWrapper.getRef())); + + case ShaderAttributeObjectRetained.TYPE_TUPLE2I: + return setUniform2iArray(ctx, shaderProgramId, loc, saa.length(), + (int[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE2F: + return setUniform2fArray(ctx, shaderProgramId, loc, saa.length(), + (float[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE3I: + return setUniform3iArray(ctx, shaderProgramId, loc, saa.length(), + (int[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE3F: + return setUniform3fArray(ctx, shaderProgramId, loc, saa.length(), + (float[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE4I: + return setUniform4iArray(ctx, shaderProgramId, loc, saa.length(), + (int[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_TUPLE4F: + return setUniform4fArray(ctx, shaderProgramId, loc, saa.length(), + (float[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_MATRIX3F: + return setUniformMatrix3fArray(ctx, shaderProgramId, loc, saa.length(), + (float[])saa.attrWrapper.getRef()); + + case ShaderAttributeObjectRetained.TYPE_MATRIX4F: + return setUniformMatrix4fArray(ctx, shaderProgramId, loc, saa.length(), + (float[])saa.attrWrapper.getRef()); + + default: + // Should never get here + assert false : "Unrecognized ShaderAttributeArray classType"; + return null; + } + + } + + + void setShaderAttributes(Canvas3D cv, ShaderAttributeSetRetained attributeSet) { + final boolean useSharedCtx = cv.useSharedCtx && cv.screen.renderer.sharedCtx != null; + final int cvRdrIndex = useSharedCtx ? cv.screen.renderer.rendererId : cv.canvasId; + ShaderProgramData spData = getShaderProgramData(cvRdrIndex); + + // Just return if shader program wasn't linked successfully + if (!spData.isLinked()) { + return; + } + + ShaderProgramId shaderProgramId = spData.getShaderProgramId(); + + Iterator attrs = attributeSet.getAttrs().values().iterator(); + while (attrs.hasNext()) { + ShaderError err = null; + ShaderAttributeRetained saRetained = attrs.next(); + + // Lookup attribute info for the specified attrName; null means + // that the name does not appear in the ShaderProgram, so we will + // report an error. + AttrNameInfo attrNameInfo = spData.getAttrNameInfo(saRetained.getAttributeName()); + if(attrNameInfo == null) { +// System.err.println("ShaderProgramRetained : attrLocation (" + saRetained.getAttributeName() + ") is null."); + String errMsg = "Attribute name not set in ShaderProgram: " + saRetained.getAttributeName(); // TODO: I18N + err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR, errMsg); + } else { + ShaderAttrLoc loc = attrNameInfo.getLocation(); + if (loc != null) { + if (saRetained instanceof ShaderAttributeValueRetained) { + ShaderAttributeValueRetained savRetained = (ShaderAttributeValueRetained)saRetained; + if (attrNameInfo.isArray() || + (savRetained.getClassType() != attrNameInfo.getType())) { + String errMsg = "Attribute type mismatch: " + savRetained.getAttributeName(); // TODO: I18N + err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_TYPE_ERROR, errMsg); + } + else { + err = setUniformAttrValue(cv.ctx, shaderProgramId, loc, savRetained); + } + } else if (saRetained instanceof ShaderAttributeArrayRetained) { + ShaderAttributeArrayRetained saaRetained = (ShaderAttributeArrayRetained)saRetained; + if (!attrNameInfo.isArray() || + (saaRetained.getClassType() != attrNameInfo.getType())) { + String errMsg = "Attribute type mismatch: " + saaRetained.getAttributeName(); // TODO: I18N + err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_TYPE_ERROR, errMsg); + } + else { + err = setUniformAttrArray(cv.ctx, shaderProgramId, loc, saaRetained); + } + } else if (saRetained instanceof ShaderAttributeBindingRetained) { + assert false; + throw new RuntimeException("not implemented"); + } else { + assert false; + } + } + } + + if (err != null) { + // Before reporting the ShaderAttribute error, check + // whether it has already been reported for this ShaderProgram + if (shaderAttrErrorSet == null) { + shaderAttrErrorSet = new HashSet(); + } + if (shaderAttrErrorSet.add((ShaderAttribute) saRetained.source)) { + err.setShaderProgram((ShaderProgram)this.source); + err.setShaderAttributeSet((ShaderAttributeSet)attributeSet.source); + err.setShaderAttribute((ShaderAttribute)saRetained.source); + err.setCanvas3D(cv); + notifyErrorListeners(cv, err); + } + } + } + } + + class ShaderProgramData extends Object { + + // issue 378 - time stamp of context creation for this Canvas + private long ctxTimeStamp; + + // shaderProgramId use by native code. + private ShaderProgramId shaderProgramId = null; + + // linked flag for native. + private boolean linked = false; + + // A map of locations for ShaderAttributes. +private HashMap attrNameInfoMap = new HashMap(); + + /** ShaderProgramData Constructor */ + ShaderProgramData() { + } + + void reset() { + ctxTimeStamp = 0L; + shaderProgramId = null; + linked = false; + attrNameInfoMap.clear(); + } + + long getCtxTimeStamp() { + return ctxTimeStamp; + } + + void setCtxTimeStamp(long ctxTimeStamp) { + this.ctxTimeStamp = ctxTimeStamp; + } + + void setShaderProgramId(ShaderProgramId shaderProgramId) { + this.shaderProgramId = shaderProgramId; + } + + ShaderProgramId getShaderProgramId() { + return this.shaderProgramId; + } + + void setLinked(boolean linked) { + this.linked = linked; + } + + boolean isLinked() { + return linked; + } + +void setAttrNameInfo(String shaderAttribute, AttrNameInfo attrNameInfo) { + assert (shaderAttribute != null); + attrNameInfoMap.put(shaderAttribute, attrNameInfo); +} + +AttrNameInfo getAttrNameInfo(String shaderAttribute) { + return attrNameInfoMap.get(shaderAttribute); +} + + } + + // Data associated with an attribute name + class AttrNameInfo { + void setLocation(ShaderAttrLoc loc) { + this.loc = loc; + } + + ShaderAttrLoc getLocation() { + return loc; + } + + void setType(int type) { + this.type = type; + } + + int getType() { + return type; + } + + boolean isArray() { + return isArray; + } + + void setArray(boolean isArray) { + this.isArray = isArray; + } + + // Location of attribute name in linked shader program + private ShaderAttrLoc loc; + + // boolean indicating whether the attribute is an array + private boolean isArray; + + // type of shader attribute + private int type; + } + +} diff --git a/src/javax/media/j3d/ShaderRetained.java b/src/javax/media/j3d/ShaderRetained.java new file mode 100644 index 0000000..e946939 --- /dev/null +++ b/src/javax/media/j3d/ShaderRetained.java @@ -0,0 +1,170 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * The ShaderRetained object is the abstract base class for programmable + * shader code. Currently, only text-based source code shaders are + * supported, so the only subclass of Shader is SourceCodeShader. We + * leave open the possibility for binary (object code) shaders in the + * future. + */ +abstract class ShaderRetained extends NodeComponentRetained { + int shadingLanguage; + int shaderType; + + // Each element in the array corresponds to a unique renderer if shared + // context or a unique canvas otherwise. + // shaderId use by native code. One per Canvas. + ShaderData[] shaderData; + + // Flag indicating whether a COMPILE_ERROR has occurred for this shader + // object. It is set in updateNative to indicate that the compileShader + // operation failed. It is cleared in setLive or clearLive. + // TODO KCR: Add code to clear this in setLive or clearLive + boolean compileErrorOccurred = false; + + // need to synchronize access from multiple rendering threads + Object resourceLock = new Object(); + + void initializeShader(int shadingLanguage, int shaderType) { + this.shadingLanguage = shadingLanguage; + this.shaderType = shaderType; + } + + int getShadingLanguage() { + return shadingLanguage; + } + + int getShaderType() { + return shaderType; + } + + @Override + void setLive(boolean inBackgroundGroup, int refCount) { + // System.err.println("SourceCodeShaderRetained.setLive()"); + super.setLive(inBackgroundGroup, refCount); + } + + @Override + void clearLive(int refCount) { + // System.err.println("SourceCodeShaderRetained.clearLive()"); + super.clearLive(refCount); + } + + /** + * Shader object doesn't really have mirror object. + * But it's using the updateMirrorObject interface to propagate + * the changes to the users + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + System.err.println("Shader.updateMirrorObject not implemented yet!"); + } + + @Override + void handleFrequencyChange(int bit) { + System.err.println("Shader.handleFrequencyChange not implemented yet!"); + } + + void createShaderData(int cvRdrIndex, long ctxTimeStamp) { + // Create shaderProgram resources if it has not been done. + synchronized(resourceLock) { + if (shaderData == null) { + shaderData = new ShaderData[cvRdrIndex+1]; + } else if (shaderData.length <= cvRdrIndex) { + ShaderData[] tempSData = new ShaderData[cvRdrIndex+1]; + + System.arraycopy(shaderData, 0, + tempSData, 0, + shaderData.length); + shaderData = tempSData; + } + + if (shaderData[cvRdrIndex] == null) { + shaderData[cvRdrIndex] = new ShaderData(); + } else if (shaderData[cvRdrIndex].getCtxTimeStamp() != ctxTimeStamp) { + // Issue 378 - reset the shader data for this canvas / renderer + // if the context has been recreated + shaderData[cvRdrIndex].reset(); + } + shaderData[cvRdrIndex].setCtxTimeStamp(ctxTimeStamp); + } + } + + + // Per-context (canvas) data for this shader + class ShaderData extends Object { + + // Issue 378 - time stamp of context creation for this canvas + private long ctxTimeStamp; + + // shaderId use by native code + private ShaderId shaderId = null; + + // indicated that the shader has been compiled for this canvas + private boolean compiled = false; + + /** ShaderProgramData Constructor */ + ShaderData() { + } + + void reset() { + ctxTimeStamp = 0L; + shaderId = null; + compiled = false; + } + + long getCtxTimeStamp() { + return ctxTimeStamp; + } + + void setCtxTimeStamp(long ctxTimeStamp) { + this.ctxTimeStamp = ctxTimeStamp; + } + + ShaderId getShaderId() { + return shaderId; + } + + void setShaderId(ShaderId shaderId) { + this.shaderId = shaderId; + } + + boolean isCompiled() { + return compiled; + } + + void setCompiled(boolean compiled) { + this.compiled = compiled; + } + + } + +} + diff --git a/src/javax/media/j3d/Shape3D.java b/src/javax/media/j3d/Shape3D.java new file mode 100644 index 0000000..9d9cd7e --- /dev/null +++ b/src/javax/media/j3d/Shape3D.java @@ -0,0 +1,794 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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: + *

    + *
  • GeometryArray (point): [Indexed]PointArray
  • + *
  • GeometryArray (line): [Indexed]{LineArray, LineStripArray}
  • + *
  • GeometryArray (polygon): [Indexed]{TriangleArray, TriangleStripArray, + * TriangleFanArray, QuadArray}
  • + *
  • CompressedGeometry
  • + *
  • Raster
  • + *
  • Text3D
  • + *
+ *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_GEOMETRY_READ, + ALLOW_APPEARANCE_READ, + ALLOW_COLLISION_BOUNDS_READ, + ALLOW_APPEARANCE_OVERRIDE_READ + }; + + /** + * Constructs a Shape3D node with default parameters. The default + * values are as follows: + *

    + * appearance : null
    + * geometry : { null }
    + * collision bounds : null
    + * appearance override enable : false
    + *
+ * 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() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((Shape3DRetained)retained).setGeometry(geometry, 0); + ((Shape3DRetained)retained).setAppearance(appearance); + } + + /** + * Creates the retained mode Shape3DRetained object that this + * Shape3D object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * cloneNode should be overridden by any user subclassed + * objects. All subclasses must have their cloneNode + * method consist of the following lines: + *

+     *     public Node cloneNode(boolean forceDuplicate) {
+     *         UserSubClass usc = new UserSubClass();
+     *         usc.duplicateNode(this, forceDuplicate);
+     *         return usc;
+     *     }
+     * 
+ * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Shape3D s = new Shape3D(); + s.duplicateNode(this, forceDuplicate); + return s; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * Shape3D + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + + + /** + * Copies all Shape3D information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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(). + */ + @Override + 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/javax/media/j3d/Shape3DCompileRetained.java b/src/javax/media/j3d/Shape3DCompileRetained.java new file mode 100644 index 0000000..599632a --- /dev/null +++ b/src/javax/media/j3d/Shape3DCompileRetained.java @@ -0,0 +1,395 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Point3d; + +/** + * 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; + // Merged list, only merged if geometry is mergeable + ArrayList[] mergedList = new ArrayList[GeometryRetained.GEO_TYPE_GEOMETRYARRAY + 1]; + // Sorted list of separate geometry by geoType + ArrayList[] separateList = new ArrayList[GeometryRetained.GEO_TYPE_GEOMETRYARRAY + 1]; + + // Assign the num of shapes + numShapes = nShapes; + + 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); + + // 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++) { + Shape3DRetained 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++) { + GeometryArrayRetained geo = (GeometryArrayRetained)shape.geometryList.get(j); + if (geo == null) + continue; + + if (shape.willRemainOpaque(geo.geoType) && geo.isMergeable()) { + if (mergedList[geo.geoType] == null) { + mergedList[geo.geoType] = new 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 + 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 = shape.geometryList.get(j); + if (g != null) + sList.add((Geometry)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++) { + switch (i) { + case GeometryArrayRetained.GEO_TYPE_QUAD_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new QuadArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_TRI_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new TriangleArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_POINT_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new PointArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_LINE_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new LineArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_TRI_STRIP_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new TriangleStripArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_TRI_FAN_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new TriangleFanArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_LINE_STRIP_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new LineStripArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_QUAD_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new IndexedQuadArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_TRI_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new IndexedTriangleArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_POINT_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new IndexedPointArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_LINE_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], new IndexedLineArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], + new IndexedTriangleStripArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_TRI_FAN_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], + new IndexedTriangleFanArrayRetained()); + addSeparateList(separateList[i]); + break; + case GeometryArrayRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET: + if (mergedList[i] != null) + addMergedList(mergedList[i], + new IndexedLineStripArrayRetained()); + addSeparateList(separateList[i]); + break; + } + } +} + +private void addMergedList(ArrayList glist, + GeometryArrayRetained cgeo) { + cgeo.setCompiled(glist); + geometryList.add(cgeo); + cgeo.setSource(((SceneGraphObjectRetained) glist.get(0)).source); +} + +private void addSeparateList(ArrayList glist) { + if (glist == null) + return; + + for (int k = 0; k < glist.size(); k++) { + geometryList.add(glist.get(k)); + } +} + @Override + Bounds getCollisionBounds(int childIndex) { + return collisionBound; + } + + +@Override +int numGeometries(int childIndex) { + return geometryInfo.get(childIndex).size(); +} + +@Override +Geometry getGeometry(int i, int childIndex) { + return geometryInfo.get(childIndex).get(i); +} + +@Override +Enumeration getAllGeometries(int childIndex) { + ArrayList geoInfo = 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) + return super.getBounds(); + + ArrayList glist = geometryInfo.get(childIndex); + if (glist == null) + return null; + + BoundingBox bbox = new BoundingBox((Bounds)null); + for (int i = 0; i < glist.size(); i++) { + Geometry g = glist.get(i); + if (g == null) + continue; + + GeometryRetained geometry = (GeometryRetained)g.retained; + if (geometry.geoType == GeometryRetained.GEO_TYPE_NONE) + continue; + + geometry.computeBoundingBox(); + synchronized (geometry.geoBounds) { + bbox.combine(geometry.geoBounds); + } + } + + return bbox; +} + + + /** + * 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 path is + * invalid. + */ + @Override + boolean intersect(SceneGraphPath path, + PickShape pickShape, double[] dist) { + + int flags; + PickInfo pickInfo = new PickInfo(); + + Transform3D localToVworld = path.getTransform(); + if (localToVworld == null) { + throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3")); + } + pickInfo.setLocalToVWorldRef( localToVworld); + + Shape3D shape = (Shape3D) path.getObject(); + // Get the geometries for this shape only, since the compiled + // geomtryList contains several shapes + ArrayList glist = geometryInfo.get(shape.id); + + // System.err.println("Shape3DCompileRetained.intersect() : "); + if (dist == null) { + // System.err.println(" no dist request ...."); + return intersect(pickInfo, pickShape, 0, glist); + } + + flags = PickInfo.CLOSEST_DISTANCE; + if (intersect(pickInfo, pickShape, flags, glist)) { + dist[0] = pickInfo.getClosestDistance(); + return true; + } + + return false; + + } + +boolean intersect(PickInfo pickInfo, PickShape pickShape, int flags, + ArrayList geometryList) { + + Transform3D localToVworld = pickInfo.getLocalToVWorldRef(); + + Transform3D t3d = new Transform3D(); + t3d.invert(localToVworld); + PickShape newPS = pickShape.transform(t3d); + + int geomListSize = geometryList.size(); + GeometryRetained geometry; + if (((flags & PickInfo.CLOSEST_INTERSECTION_POINT) == 0) && + ((flags & PickInfo.CLOSEST_DISTANCE) == 0) && + ((flags & PickInfo.CLOSEST_GEOM_INFO) == 0) && + ((flags & PickInfo.ALL_GEOM_INFO) == 0)) { + + for (int i=0; i < geomListSize; i++) { + geometry = (GeometryRetained) geometryList.get(i).retained; + if (geometry != null) { + if (geometry.mirrorGeometry != null) { + geometry = geometry.mirrorGeometry; + } + // Need to modify this method + // if (geometry.intersect(newPS, null, null)) { + if (geometry.intersect(newPS, null, 0, null, null, 0)) { + return true; + } + } + } + } + else { + double distance; + double minDist = Double.POSITIVE_INFINITY; + Point3d closestIPnt = new Point3d(); + Point3d iPnt = new Point3d(); + Point3d iPntVW = new Point3d(); + + for (int i=0; i < geomListSize; i++) { + geometry = (GeometryRetained) geometryList.get(i).retained; + if (geometry != null) { + if (geometry.mirrorGeometry != null) { + geometry = geometry.mirrorGeometry; + } + if (geometry.intersect(newPS, pickInfo, flags, iPnt, geometry, i)) { + + iPntVW.set(iPnt); + localToVworld.transform(iPntVW); + distance = pickShape.distance(iPntVW); + + if (minDist > distance) { + minDist = distance; + closestIPnt.set(iPnt); + } + } + } + } + + if (minDist < Double.POSITIVE_INFINITY) { + if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) { + pickInfo.setClosestDistance(minDist); + } + if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) { + pickInfo.setClosestIntersectionPoint(closestIPnt); + } + return true; + } + } + + return false; + + } + +} diff --git a/src/javax/media/j3d/Shape3DRetained.java b/src/javax/media/j3d/Shape3DRetained.java new file mode 100644 index 0000000..7af82a7 --- /dev/null +++ b/src/javax/media/j3d/Shape3DRetained.java @@ -0,0 +1,2844 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Point3d; + +/** + * 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 = new J3dMessage(); + 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); + } + } + + @Override + 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 + */ + @Override + void setBounds(Bounds bounds) { + super.setBounds(bounds); + + if (source.isLive() && !boundsAutoCompute) { + J3dMessage message = new J3dMessage(); + 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) { + 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); + } + } + dirtyBoundsCache(); + } + + /** + * 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 = geometryList.get(index); + if (oldGeom != null) { + oldGeom.clearLive(refCount); + for (i=0; i geomList = new Vector(geometryList.size()); + + for (int i = 0; i < geometryList.size(); i++) { + GeometryRetained ga = 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 distance) { + minDist = distance; + closestIPnt.set(iPnt); + } + } + } + } + + if (minDist < Double.POSITIVE_INFINITY) { + if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) { + pickInfo.setClosestDistance(minDist); + } + if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) { + pickInfo.setClosestIntersectionPoint(closestIPnt); + } + return true; + } + } + + return false; + + } + + + /** + * 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 path is + * invalid. + */ + + boolean intersect(SceneGraphPath path, + PickShape pickShape, double[] dist) { + + int flags; + PickInfo pickInfo = new PickInfo(); + + Transform3D localToVworld = path.getTransform(); + if (localToVworld == null) { + throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3")); + } + pickInfo.setLocalToVWorldRef( localToVworld); + //System.err.println("Shape3DRetained.intersect() : "); + if (dist == null) { + //System.err.println(" no dist request ...."); + return intersect(pickInfo, pickShape, 0); + } + + flags = PickInfo.CLOSEST_DISTANCE; + if (intersect(pickInfo, pickShape, flags)) { + dist[0] = pickInfo.getClosestDistance(); + return true; + } + + return 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); + } + + /** + * 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; + + // New 1.3.2 + // Used when user supplied their own bounds for transparency sorting + // GeometryAtom uses this to change how it computes the centroid + ms.boundsAutoCompute = boundsAutoCompute; + ms.localBounds = localBounds; + // End new 1.3.2 + + OrderedPath op = s.orderedPaths.get(index); + if (op.pathElements.size() == 0) { + ms.orderedPath = null; + } else { + ms.orderedPath = op; +/* + System.err.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(); + + Shape3DRetained[] msArr = (Shape3DRetained[]) objs[2]; + int i; + 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 + */ + + @Override + Bounds getBounds() { + + if(boundsAutoCompute) { + // System.err.println("getBounds ---- localBounds is " + localBounds); + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + if (validCachedBounds) { + return (Bounds) cachedBounds.clone(); + } + + if(geometryList != null) { + BoundingBox bbox = new BoundingBox((Bounds) null); + GeometryRetained geometry; + for(int i=0; i msList = new ArrayList(); + + super.doSetLive(s); + + nodeId = universe.getNodeId(); + + + if (inSharedGroup) { + for (i=0; i l = s.lights.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addLight(l.get(m)); + } + } + } + + // Add any scoped fog + if (s.fogs != null) { + ArrayList l = s.fogs.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addFog(l.get(m)); + } + } + } + + // Add any scoped modelClip + if (s.modelClips != null) { + ArrayList l = s.modelClips.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addModelClip(l.get(m)); + } + } + } + + // Add any scoped alt app + if (s.altAppearances != null) { + ArrayList l = s.altAppearances.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addAltApp(l.get(m)); + } + } + } + synchronized(mirrorShape3D) { + mirrorShape3D.add(j,shape); + } + + msList.add(shape); + if (s.viewLists != null) { + shape.viewList = 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 = 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 = s.lights.get(0); + for (i = 0; i < l.size(); i++) { + shape.addLight(l.get(i)); + } + } + + // Add any scoped fog + if (s.fogs != null) { + ArrayList l = s.fogs.get(0); + for (i = 0; i < l.size(); i++) { + shape.addFog(l.get(i)); + } + } + + // Add any scoped modelClip + if (s.modelClips != null) { + ArrayList l = s.modelClips.get(0); + for (i = 0; i < l.size(); i++) { + shape.addModelClip(l.get(i)); + } + + } + + // Add any scoped alt app + if (s.altAppearances != null) { + ArrayList l = s.altAppearances.get(0); + for (i = 0; i < l.size(); i++) { + shape.addAltApp(l.get(i)); + } + } + synchronized(mirrorShape3D) { + mirrorShape3D.add(shape); + } + + msList.add(shape); + if (s.viewLists != null) + shape.viewList = 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 = s.switchStates.get(0); + } + + for (k = 0; k < msList.size(); k++) { + Shape3DRetained sh = 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 msList = new ArrayList(); + + super.clearLive(s); + + + + if (inSharedGroup) { + synchronized(mirrorShape3D) { + Shape3DRetained[] shapes = mirrorShape3D.toArray(new Shape3DRetained[mirrorShape3D.size()]); + for (i=0; i 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; + } + + + @Override + 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 = geometryList.get(i); + if (geo != null) + geo.compile(compState); + } + + } + + @Override + 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; + 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 = geometryList.get(index); + index++; + } + + if (!(geometry instanceof GeometryArrayRetained)) { + return false; + } + + GeometryArrayRetained firstGeo = (GeometryArrayRetained) geometry; + + for(i=index; (i CAN NEVER BE TRUE"); + return; + } + else { + ms = getMirrorShape(k); + } + } + else { + ms = mirrorShape3D.get(0); + } + + list.add(getGeomAtom(ms)); + + } + + + // Called on the mirror Object + void addLight(LightRetained light) { + LightRetained[] newlights; + int i; + + if (lights == null) { + lights = new LightRetained[10]; + } + else if (lights.length == numlights) { + newlights = new LightRetained[numlights*2]; + for (i=0; i=0) { + return mirrorShape3D.get(i); + } + } + // Not possible + throw new RuntimeException("Shape3DRetained: MirrorShape Not found!"); + } + + @Override + 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 gaList = otherShape.geometryList; + int gaSize = gaList.size(); + Transform3D otherLocalToVworld = otherShape.getCurrentLocalToVworld(); + Transform3D thisLocalToVworld = getCurrentLocalToVworld(); + 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 = geometryList.get(i); + if (geom1 != null) { + for (int j=gaSize-1; j >=0; j--) { + geom2 = 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 = 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; + + universe = morph.universe; + inSharedGroup = morph.inSharedGroup; + inBackgroundGroup = morph.inBackgroundGroup; + geometryBackground = morph.geometryBackground; + parent = morph.parent; + locale = morph.locale; + + OrderedPath op = 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 = 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; + 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 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 = 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 = 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 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.err.println("Shape3DRetained - bounds (Before) " + bounds); + bounds.setLower(-maxVal, -maxVal, -maxVal); + bounds.setUpper(maxVal, maxVal, maxVal); + // System.err.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= 0; i--) { + GeometryRetained geomRetained = 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 = geometryList.get(index); + if (oldGeom != null) { + oldGeom.clearLive(refCount); + oldGeom.decRefCnt(); + for (i=0; i= 0; index--) { + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.decRefCnt(); + } + geometryList.remove(index); + } + } + dirtyBoundsCache(); + } + + boolean willRemainOpaque(int geoType) { + if (appearance == null || + (appearance.isStatic() && + appearance.isOpaque(geoType))) { + return true; + } + else { + return false; + } + + } + + @Override + 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.err.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; + } + + @Override + void searchGeometryAtoms(UnorderList list) { + list.add(getGeomAtom(getMirrorShape(key))); + } +} + diff --git a/src/javax/media/j3d/SharedGroup.java b/src/javax/media/j3d/SharedGroup.java new file mode 100644 index 0000000..8d0fd7c --- /dev/null +++ b/src/javax/media/j3d/SharedGroup.java @@ -0,0 +1,168 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + *

    + *
  • 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.
  • + * + *

  • A SharedGroup may be compiled by calling its compile method + * prior to being referenced by any Link leaf nodes.
  • + * + *

  • Only Link leaf nodes may refer to SharedGroup nodes. A + * SharedGroup node cannot have parents or be attached to a Locale.
  • + *

+ * + * 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: + *

    + *
  • Light
  • + *
  • Link
  • + *
  • Morph
  • + *
  • Shape
  • + *
  • Sound

+ * + * An IllegalSharingException is thrown if any of the following leaf nodes + * appear in a shared subgraph:

+ *

    + *
  • AlternateAppearance
  • + *
  • Background
  • + *
  • Behavior
  • + *
  • BoundingLeaf
  • + *
  • Clip
  • + *
  • Fog
  • + *
  • ModelClip
  • + *
  • Soundscape
  • + *
  • ViewPlatform
+ *

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_LINK_READ + }; + + /** + * Constructs and initializes a new SharedGroup node object. + */ + public SharedGroup() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + + /** + * 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. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + SharedGroup sg = new SharedGroup(); + sg.duplicateNode(this, forceDuplicate); + return sg; + } +} diff --git a/src/javax/media/j3d/SharedGroupRetained.java b/src/javax/media/j3d/SharedGroupRetained.java new file mode 100644 index 0000000..1d5b3d3 --- /dev/null +++ b/src/javax/media/j3d/SharedGroupRetained.java @@ -0,0 +1,931 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +/** + * 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. + @Override + 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.err.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); + } + } + + + @Override + 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(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= 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; + MasterControl.getCoreLogger().severe("Can't Find matching hashKey in SG.removeNodeData."); + } + } + + 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; + } + + @Override + 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; + + + // XXXX: 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 updateList) { + + SwitchRetained sw; + LinkRetained ln; + Object obj; + int i,j,k; + + ArrayList childSwitchLinks = childrenSwitchLinks.get(child); + for (i=0; i>(1); + } + childrenSwitchLinks.add(index, new ArrayList(1)); + } + + @Override + void appendChildrenData() { + if (childrenSwitchLinks == null) { + childrenSwitchLinks = new ArrayList>(1); + } + childrenSwitchLinks.add(new ArrayList(1)); + } + + @Override + void removeChildrenData(int index) { + ArrayList oldSwitchLinks = childrenSwitchLinks.get(index); + oldSwitchLinks.clear(); + childrenSwitchLinks.remove(index); + } + + + // *************************** + // TargetsInterface methods + // *************************** + + @Override + public int getTargetThreads(int type) { + if (type == TargetsInterface.TRANSFORM_TARGETS) { + return targetThreads; + } else { + System.err.println("getTargetThreads: wrong arguments"); + return -1; + } + } + + @Override + TargetsInterface getClosestTargetsInterface(int type) { + return this; + } + + // re-evalute localTargetThreads using newCachedTargets and + // re-evaluate targetThreads + @Override + public void computeTargetThreads(int type, + CachedTargets[] newCachedTargets) { + + localTargetThreads = 0; + if (type == TargetsInterface.TRANSFORM_TARGETS) { + for(int i=0; i getTargetsData(int type, int index) { + // index is ignores for SharedGroup + if (type == TargetsInterface.SWITCH_TARGETS) { + return switchStates; + } + else { + System.err.println("getTargetsData: wrong arguments"); + return null; + } +} + + @Override + void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) { + + int i; + s.childSwitchLinks = 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); + } + + /** + * Make the boundsCache of this node and all its parents dirty + */ + @Override + void dirtyBoundsCache() { + // Possible optimisation is to not traverse up the tree + // if the cachedBounds==null. However this is not the case + // if the node is the child of a SharedGroup + if (VirtualUniverse.mc.cacheAutoComputedBounds) { + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + validCachedBounds = false; + synchronized(parents) { + Enumeration e = parents.elements(); + while(e.hasMoreElements()) { + LinkRetained parent = (LinkRetained) e.nextElement(); + if (parent!=null) { + parent.dirtyBoundsCache(); + } + } + } + } + } +} diff --git a/src/javax/media/j3d/Sound.java b/src/javax/media/j3d/Sound.java new file mode 100644 index 0000000..612206c --- /dev/null +++ b/src/javax/media/j3d/Sound.java @@ -0,0 +1,1197 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * Sound Data + * + *

    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:

    + * 1) querying the sound's duration (Sound.DURATION_UNKNOWN will be returned),
    + * 2) looping over a range of the streaming data; and
    + * 3) restart a previously played portion of the data.

    + * Depending on the implementation of the AudioDevice used, streamed, non- + * cached data may not be fully spatialized.

+ *

+ * Initial Gain + * + *

    This gain is a scale factor applied to the sound data associated + * with this sound source to increase or decrease its overall amplitude.
+ *

+ * Loop + * + *

    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. + *

    + * 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. + *

    + * 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.

+ *

+ * Release Flag + * + *

    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.
+ *

+ * Continuous Flag + * + *

    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.
+ *

+ * Enable Sound + * + *

    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. + *

    + * 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.

+ *

+ * Mute Sound + * + *

    When the mute state is set true, a playing sound is made to play silently. + *

+ * Pause Sound + * + *

    When the pause state is set true, a playing sound is paused. + *

    + * 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.

+ *

+ * Scheduling Bounds + * + *

    + * 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.
+ *

+ * Scheduling Bounding Leaf + * + *

    When set to a value other than null, the scheduling bounding leaf + * region overrides the scheduling bounds + * object.
+ *

+ * Prioritize Sound + * + *

    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. + *

    + * 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). + *

    + * Sounds given the same priority are ordered randomly. If the application + * wants a specific ordering, it must assign unique priorities to each sound. + *

    + * 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.

+ *

+ * Duration + * + *

    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.
+ *

+ * Number of Channels used on Audio Device to Play Sound + * + *

    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().
+ *

+ * Preparing a Sound to be Played + * + *

    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:

    + * 1) the Sound node has non-null sound data associated with it
    + * 2) the Sound node is live
    + * 3) there is an active View in the Universe and
    + * 4) there is an initialized AudioDevice associated with the + * PhysicalEnvironment.

    + * 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).

+ *

+ * Playing Status + * + *

    A sound source will not be heard unless it is:

    + * 1) enabled/started
    + * 2) activated
    + * 3) not muted
    + * 4) not paused

    + * While these conditions are meet, the sound is potentially audible + * and the method isPlaying() will return a status of true. + *

    + * isPlaying returns false but isPlayingSilently returns true if a sound:

    + * 1) is enabled before it is activated; it is begun playing silently.
    + * 2) is enabled then deactivated while playing; it continues playing silently
    + * 3) is enabled while it mute state is true + *

    + * When the sound finishes playing it's sound data (including all loops), it + * is implicitly disabled.

+ *

+ * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_CHANNELS_USED_READ, + ALLOW_CONT_PLAY_READ, + ALLOW_DURATION_READ, + ALLOW_ENABLE_READ, + ALLOW_INITIAL_GAIN_READ, + ALLOW_IS_PLAYING_READ, + ALLOW_IS_READY_READ, + ALLOW_LOOP_READ, + ALLOW_MUTE_READ, + ALLOW_PAUSE_READ, + ALLOW_PRIORITY_READ, + ALLOW_RATE_SCALE_FACTOR_READ, + ALLOW_RELEASE_READ, + ALLOW_SCHEDULING_BOUNDS_READ, + ALLOW_SOUND_DATA_READ + }; + + + /** + * Constructs and initializes a new Sound node using default + * parameters. The following defaults values are used: + *

    + * sound data: null
    + * initial gain: 1.0
    + * loop: 0
    + * release flag: false
    + * continuous flag: false
    + * enable flag: false
    + * scheduling bounds : null
    + * scheduling bounding leaf : null
    + * priority: 1.0
    + * rate scale factor: 1.0
    + * mute state: false
    + * pause state: false
    + *
+ */ + public Sound() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 ) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/SoundException.java b/src/javax/media/j3d/SoundException.java new file mode 100644 index 0000000..44241ea --- /dev/null +++ b/src/javax/media/j3d/SoundException.java @@ -0,0 +1,49 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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/javax/media/j3d/SoundRetained.java b/src/javax/media/j3d/SoundRetained.java new file mode 100644 index 0000000..246e9dc --- /dev/null +++ b/src/javax/media/j3d/SoundRetained.java @@ -0,0 +1,1316 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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 = new J3dMessage(); + 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 = new J3dMessage(); + 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"); + } + // XXXX: 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; + } + // XXXX: 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. + @Override + 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 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; // XXXX: 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 + * 1) the Sound node has a non-null sound data and this data has + * sucessfully been loaded/opened/copied/attached;
+ * 2) the Sound node is live;
+ * 3) there is at least one active View in the Universe; and
+ * 4) an instance of an AudioDevice is attached to the current + * PhysicalEnvironment. + * + * + * @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 + * 1) the Sound node has a non-null sound data and this data has + * sucessfully been loaded/opened/copied/attached;
+ * 2) the Sound node is live;
+ * 3) the given View is active in the Universe; and
+ * 4) an instance of an AudioDevice is attached to the current + * PhysicalEnvironment. + * + * + * @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 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); + + // Assertion check view & universe + if (v == null) { + System.err.println("WARNING: SoundScheduler constructed with null view"); + } + if (u == null) { + System.err.println("WARNING: SoundScheduler constructed with null universe"); + } + + universe = u; + view = v; + reset(); + } + + + // NOTE: processMessage only called with updatethread.active true + @Override + 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(); + } + catch (Error e) { + // Issue 264 - catch Error + System.err.println("Error 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]; + + for (int i=0; i vl = 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 + */ + @Override + void removeNodes(J3dMessage m) { + Object[] nodes = (Object[])m.args[0]; + ArrayList viewScopedNodes = (ArrayList)m.args[3]; + ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; + + for (int i=0; i vl = 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 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 = prioritizedSounds.get(i); + if (soundAtom.sound.sgSound == soundRetained) { + // ignore soundRetained.release + // flag which is not implement + turnOff(soundAtom); + // Fix to Issue 431. + soundAtom.enable(soundRetained.enable); + } + } + } + } + } + } + + 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 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(); + } + // XXXX: 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 canvases = view.getAllCanvas3Ds(); + while (canvases.hasMoreElements()) { + Canvas3D canvas = 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 = 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 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) { +/* + // XXXX: 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; jj0 && 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>>>>>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; itotalChannels) { + 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 = J3dClock.currentTimeMillis(); + long shortestTime = -1L; + SoundSchedulerAtom soundAtom; + synchronized (prioritizedSounds) { + int nAtoms = prioritizedSounds.size(); + for (int i=0; i= 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 ~/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); + // XXXX: 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 || + // XXXX: 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; + + // XXXX: 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 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.err.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; + } + } + } + + @Override + void cleanup() { + // clean up any messages that are queued up, since they are + // irrelevant + // clearMessages(); + } +} diff --git a/src/javax/media/j3d/SoundSchedulerAtom.java b/src/javax/media/j3d/SoundSchedulerAtom.java new file mode 100644 index 0000000..3be6637 --- /dev/null +++ b/src/javax/media/j3d/SoundSchedulerAtom.java @@ -0,0 +1,707 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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 = J3dClock.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)"); + } + } + + +// XXXX: 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; + } + } + +// XXXX: 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; + } + } + +// XXXX: 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 + +// XXXX: isPLaying +// XXXX: setLoadingState + + // Debug print mechanism for Sound nodes + static final boolean debugFlag = false; + static final boolean internalErrors = false; + + void debugPrint(String message) { + if (debugFlag) { + System.err.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/javax/media/j3d/SoundStructure.java b/src/javax/media/j3d/SoundStructure.java new file mode 100644 index 0000000..05df38f --- /dev/null +++ b/src/javax/media/j3d/SoundStructure.java @@ -0,0 +1,741 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +/** + * 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"); + } + + @Override + 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: + // XXXX: this needs to be changed + changeNodeAttrib(m); + break; + case J3dMessage.TRANSFORM_CHANGED: + transformMsg = true; + break; + case J3dMessage.SWITCH_CHANGED: + // This method isn't implemented yet. + // processSwitchChanged(m); + // may need to process dirty switched-on transform + if (universe.transformStructure.getLazyUpdate()) { + transformMsg = true; + } + break; + case J3dMessage.VIEWSPECIFICGROUP_CHANGED: + updateViewSpecificGroupChanged(m); + break; + // XXXX: 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]; + + for (int i=0; i vl = scopedNodesViewList.get(i); + int vsize = vl.size(); + if (node instanceof SoundRetained) { + ((SoundRetained)node).isViewScoped = true; + for (int k = 0; k < vsize; k++) { + 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 = vl.get(k); + addScopedSoundscape((SoundscapeRetained) node, view); + } + } + } + } + /* + // XXXX: + 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); + } + + + @Override + void removeNodes(J3dMessage m) { + Object[] nodes = (Object[])m.args[0]; + ArrayList viewScopedNodes = (ArrayList)m.args[3]; + ArrayList> scopedNodesViewList = (ArrayList>)m.args[4]; + + for (int i=0; i vl = 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 = 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 = 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) { + // XXXX: 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(); +/* + } +*/ +// XXXX: 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 = views[j]; +// XXXX: 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 = 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 = 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 = views[j]; + v.soundScheduler.pauseSound(sound); + } + } + } + + // Implementation be needed. + 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.err.println("SoundStructure.switch on"); + // add To Schedule List + } else { + // System.err.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; + + } + + +/* +// XXXX: 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 + * 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. + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_APPLICATION_BOUNDS_READ, + ALLOW_ATTRIBUTES_READ + }; + /** + * Constructs and initializes a new Sound node using following + * defaults: + *

    application region: null (no active region)
+ *
    aural attributes: null (uses default aural attributes)
+ */ + public Soundscape() { + // Just use default values + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((SoundscapeRetained)this.retained).setApplicationBounds(region); + ((SoundscapeRetained)this.retained).setAuralAttributes(attributes); + } + + /** + * Creates the retained mode SoundscapeRetained object that this + * component object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Soundscape s = new Soundscape(); + s.duplicateNode(this, forceDuplicate); + return s; + } + + /** + * Copies all node information from originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method. + *

+ * For any NodeComponent objects + * contained by the object being duplicated, each NodeComponent + * object's duplicateOnCloneTree value is used to determine + * whether the NodeComponent 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 + * forceDuplicate parameter in the cloneTree + * method to true. + *
+ * NOTE: Applications should not 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @exception ClassCastException if originalNode is not an instance of + * Soundscape + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public void duplicateNode(Node originalNode, boolean forceDuplicate) { + checkDuplicateNode(originalNode, forceDuplicate); + } + + /** + * Copies all Soundscape information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/SoundscapeRetained.java b/src/javax/media/j3d/SoundscapeRetained.java new file mode 100644 index 0000000..dc74bf0 --- /dev/null +++ b/src/javax/media/j3d/SoundscapeRetained.java @@ -0,0 +1,458 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = new J3dMessage(); + 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((Bounds)null); + } + + + /** + * 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. + @Override + 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 + @Override + 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 + */ + @Override + 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 = 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 + */ + @Override + 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; + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + if (applicationRegion != null) { + applicationRegion.transform(xform.transform); + } + } + + @Override + void getMirrorObjects(ArrayList leafList, HashKey key) { + leafList.add(this); + } +} diff --git a/src/javax/media/j3d/SourceCodeShader.java b/src/javax/media/j3d/SourceCodeShader.java new file mode 100644 index 0000000..afc243b --- /dev/null +++ b/src/javax/media/j3d/SourceCodeShader.java @@ -0,0 +1,138 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The SourceCodeShader object is a shader that is defined using + * text-based source code. It is used to define the source code for + * both vertex and fragment shaders. The currently supported shading + * languages are Cg and GLSL. + * + * @see ShaderProgram + * + * @since Java 3D 1.4 + */ + +public class SourceCodeShader extends Shader { + + /** + * Not a public constructor, for internal use + */ + SourceCodeShader() { + } + + /** + * Constructs a new shader object of the specified shading + * language and shader type from the specified source string. + * + * @param shadingLanguage the specified shading language, one of: + * SHADING_LANGUAGE_GLSL or + * SHADING_LANGUAGE_CG. + * + * @param shaderType the shader type, one of: + * SHADER_TYPE_VERTEX or + * SHADER_TYPE_FRAGMENT. + * + * @param shaderSource the shader source code + * + * @exception NullPointerException if shaderSource is null. + */ + + public SourceCodeShader(int shadingLanguage, int shaderType, String shaderSource) { + super(shadingLanguage, shaderType); + if (shaderSource == null) { + throw new NullPointerException(); + } + ((SourceCodeShaderRetained)this.retained).initShaderSource(shaderSource); + } + + /** + * Retrieves the shader source string from this shader object. + * + * @return the shader source string. + */ + public String getShaderSource() { + return ((SourceCodeShaderRetained)this.retained).getShaderSource(); + } + + + /** + * Creates a retained mode SourceCodeShaderRetained object that this + * SourceCodeShader component object will point to. + */ + @Override + void createRetained() { + this.retained = new SourceCodeShaderRetained(); + this.retained.setSource(this); + // System.err.println("SourceCodeShader.createRetained()"); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + SourceCodeShaderRetained scsRetained = (SourceCodeShaderRetained) retained; + + SourceCodeShader scs = new SourceCodeShader(scsRetained.getShadingLanguage(), + scsRetained.getShaderType(), + scsRetained.getShaderSource()); + scs.duplicateNodeComponent(this); + return scs; + } + + + /** + * Copies all node information from originalNodeComponent + * into the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + void duplicateAttributes(NodeComponent originalNodeComponent, + boolean forceDuplicate) { + super.duplicateAttributes(originalNodeComponent, forceDuplicate); + + String sc = ((SourceCodeShaderRetained) originalNodeComponent.retained).getShaderSource(); + + if (sc != null) { + ((SourceCodeShaderRetained) retained).setShaderSource(sc); + } + } + +} diff --git a/src/javax/media/j3d/SourceCodeShaderRetained.java b/src/javax/media/j3d/SourceCodeShaderRetained.java new file mode 100644 index 0000000..2e20b8e --- /dev/null +++ b/src/javax/media/j3d/SourceCodeShaderRetained.java @@ -0,0 +1,102 @@ +/* + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The SourceCodeShaderRetained object is a shader that is defined using + * text-based source code. It is used to define the source code for + * both vertex and fragment shaders. The currently supported shading + * languages are Cg and GLSL. + */ + +class SourceCodeShaderRetained extends ShaderRetained { + + private String shaderSource = null; + + /** + * Constructs a new shader retained object of the specified shading + * language and shader type from the specified source string. + */ + + SourceCodeShaderRetained() { + } + + // This method is similar to setShaderSource(). + // To conform to j3d frame in retained creation, we will stick with method + // with init name. + final void initShaderSource(String shaderSource) { + this.shaderSource = shaderSource; + } + + final void set(int shadingLanguage, int shaderType, String shaderSource) { + this.shadingLanguage = shadingLanguage; + this.shaderType = shaderType; + this.shaderSource = shaderSource; + } + + /** + * Retrieves the shader source string from this shader object. + * + * @return the shader source string. + */ + final String getShaderSource() { + return shaderSource; + } + + final void setShaderSource(String shaderSource) { + this.shaderSource = shaderSource; + } + + @Override + synchronized void createMirrorObject() { + // System.err.println("SourceCodeShaderRetained : createMirrorObject"); + + if (mirror == null) { + SourceCodeShaderRetained mirrorSCS = new SourceCodeShaderRetained(); + mirror = mirrorSCS; + } + + initMirrorObject(); + } + + /** + * Initializes a mirror object. + */ + @Override + synchronized void initMirrorObject() { + mirror.source = source; + + ((SourceCodeShaderRetained) mirror).set(shadingLanguage, shaderType, shaderSource); + ((SourceCodeShaderRetained) mirror).shaderData = null; + } + + @Override + synchronized void updateMirrorObject(int component, Object value) { + System.err.println("SourceCodeShader.updateMirrorObject not implemented yet!"); + } + +} diff --git a/src/javax/media/j3d/SpotLight.java b/src/javax/media/j3d/SpotLight.java new file mode 100644 index 0000000..f9e65e9 --- /dev/null +++ b/src/javax/media/j3d/SpotLight.java @@ -0,0 +1,375 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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:

+ *

    + *
  • 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).
  • + *

    + *

  • 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.
  • + *

    + *

  • 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.
  • + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SPREAD_ANGLE_READ, + ALLOW_CONCENTRATION_READ, + ALLOW_DIRECTION_READ + }; + + /** + * Constructs a SpotLight node with default parameters. + * The default values are as follows: + *
    + * direction : (0,0,-1)
    + * spread angle : PI radians
    + * concentration : 0.0
    + *
+ */ + public SpotLight() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + SpotLight s = new SpotLight(); + s.duplicateNode(this, forceDuplicate); + return s; + } + + + /** + * Copies all SpotLight information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/SpotLightRetained.java b/src/javax/media/j3d/SpotLightRetained.java new file mode 100644 index 0000000..f7fef46 --- /dev/null +++ b/src/javax/media/j3d/SpotLightRetained.java @@ -0,0 +1,322 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Vector3f; + +/** + * 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; + } + } + + + @Override + 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. + */ + @Override + void update(Context ctx, int lightSlot, double scale) { + validateAttenuationInEc(scale); + Pipeline.getPipeline().updateSpotLight(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. + */ + @Override + // 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); + } + + + // Clones only the retained side, internal use only + @Override + protected Object clone() { + SpotLightRetained sr = (SpotLightRetained)super.clone(); + sr.direction = new Vector3f(direction); + sr.xformDirection = new Vector3f(); + return sr; + } + + + + // Called on the mirror object + @Override + void updateTransformChange() { + super.updateTransformChange(); + + getLastLocalToVworld().transform(direction, xformDirection); + xformDirection.normalize(); + + } + + @Override + final void sendMessage(int attrMask, Object attr) { + J3dMessage createMessage = new J3dMessage(); + 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); + } + + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + xform.transform.transform(direction, direction); + } +} diff --git a/src/javax/media/j3d/StructureUpdateThread.java b/src/javax/media/j3d/StructureUpdateThread.java new file mode 100644 index 0000000..8dbf431 --- /dev/null +++ b/src/javax/media/j3d/StructureUpdateThread.java @@ -0,0 +1,100 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + } + + } + + @Override + void doWork(long referenceTime) { + structure.processMessages(referenceTime); + } +} diff --git a/src/javax/media/j3d/Switch.java b/src/javax/media/j3d/Switch.java new file mode 100644 index 0000000..6a3f760 --- /dev/null +++ b/src/javax/media/j3d/Switch.java @@ -0,0 +1,277 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_SWITCH_READ + }; + + /** + * Constructs a Switch node with default parameters. + * The default values are as follows: + *

    + * child selection index : CHILD_NONE
    + * child selection mask : false (for all children)
    + *
+ */ + public Switch() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs and initializes a Switch node using the specified + * child selection index. + * @param whichChild the initial child selection index + */ + public Switch(int whichChild) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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){ + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((SwitchRetained)this.retained).setWhichChild(whichChild, true); + ((SwitchRetained)this.retained).setChildMask(childMask); + } + + /** + * Creates the retained mode SwitchRetained object that this + * Switch object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + Switch s = new Switch(); + s.duplicateNode(this, forceDuplicate); + return s; + } + + /** + * Copies all Switch information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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/javax/media/j3d/SwitchRetained.java b/src/javax/media/j3d/SwitchRetained.java new file mode 100644 index 0000000..eac2cb6 --- /dev/null +++ b/src/javax/media/j3d/SwitchRetained.java @@ -0,0 +1,957 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; + +/** + * 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 this.childMask.size()) { + nbits = childMask.size(); + } else { + nbits = this.childMask.size(); + } + + for (i=0; i updateList = new ArrayList(1); + nchildren = children.size(); + for (i=0; i updateList) { + + J3dMessage m ; + int i,j,size,threads; + Object[] nodesArr, nodes; + + threads = updateTargets.computeSwitchThreads(); + + if (threads > 0) { + + m = new J3dMessage(); + 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= 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 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= 0) { + setAuxData(s, j, hkIndex); + } else { + MasterControl.getCoreLogger().severe("Can't Find matching hashKey in setNodeData."); + } + s.hashkeyIndex[j] = hkIndex; + } + } + } + + @Override + 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 = Arrays.copyOf(savedClosestSwitchIndices, savedClosestSwitchIndices.length); + + // 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 switchStates; + + if (refCount <= 0) { + // remove this node from parentSwitchLink's childSwitchLinks + // clear childSwitchLinks + if (parentSwitchLink != null) { + for(i=0; i switchLinks = parentSwitchLink.childrenSwitchLinks.get(i); + if (switchLinks.contains(this)) { + switchLinks.remove(this); + break; + } + } + } + for (j=0; j= 0; i--) { + hkIndex = s.keys[i].equals(localToVworldKeys, 0, + localToVworldKeys.length); + if(hkIndex >= 0) { + for (j=0; j updateList) { + int i,j,k; + SwitchRetained sw; + LinkRetained ln; + Object obj; + + boolean newSwChanged = false; + ArrayList childSwitchStates = childrenSwitchStates.get(child); + 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); + } + } + } + + + ArrayList childSwitchLinks = childrenSwitchLinks.get(child); + int cslSize =childSwitchLinks.size(); + for (i=0; i= 0 && + whichChild < children.size()) { + + NodeRetained child = children.get(whichChild); + if (child != null) { + child.computeCombineBounds(boundingObject); + } + } + + return boundingObject; + } 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 + */ + @Override + void compile(CompileState compState) { + + + super.compile(compState); + + // don't remove this group node + mergeFlag = SceneGraphObjectRetained.DONT_MERGE; + + if (J3dDebug.devPhase && J3dDebug.debug) { + compState.numSwitches++; + } + } + + @Override + 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>(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 oldSwitchStates = childrenSwitchStates.get(index); + oldSwitchStates.clear(); + childrenSwitchStates.remove(index); + + ArrayList oldSwitchLinks = childrenSwitchLinks.get(index); + oldSwitchLinks.clear(); + childrenSwitchLinks.remove(index); + } + + @Override + void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) { + + int numPaths = (inSharedGroup)? s.keys.length : 1; + s.childSwitchLinks = 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 switchStates = childrenSwitchStates.get(child); + if (index < switchStates.size()) { + SwitchState switchState = switchStates.get(index); + return switchState.cachedTargets; + } else { + return null; + } + } else { + System.err.println("getCachedTargets: wrong arguments"); + return null; + } + } + + @Override + public void resetCachedTargets(int type, + CachedTargets[] newCtArr, int child) { + if (type == TargetsInterface.SWITCH_TARGETS) { + ArrayList switchStates = childrenSwitchStates.get(child); + if (newCtArr.length != switchStates.size()) { + System.err.println("resetCachedTargets: unmatched length!" + + newCtArr.length + " " + switchStates.size()); + System.err.println(" resetCachedTargets: " + this); + } + SwitchState switchState; + for (int i=0; i getTargetsData(int type, int child) { + if (type == TargetsInterface.SWITCH_TARGETS) { + return childrenSwitchStates.get(child); + } + else { + System.err.println("getTargetsData: wrong arguments"); + return null; + } +} + + @Override + public int getTargetThreads(int type) { + System.err.println("getTargetsThreads: wrong arguments"); + return -1; + } + + @Override + public void updateCachedTargets(int type, CachedTargets[] newCt) { + System.err.println("updateCachedTarget: wrong arguments"); + } + + @Override + public void computeTargetThreads(int type, CachedTargets[] newCt) { + System.err.println("computeTargetThreads: wrong arguments"); + } + + @Override + public void updateTargetThreads(int type, CachedTargets[] newCt) { + System.err.println("updateTargetThreads: wrong arguments"); + } + + @Override + public void propagateTargetThreads(int type, int newTargetThreads) { + System.err.println("propagateTargetThreads: wrong arguments"); + } + + @Override + public void copyCachedTargets(int type, CachedTargets[] newCt) { + System.err.println("copyCachedTarget: wrong arguments"); + } +} diff --git a/src/javax/media/j3d/SwitchState.java b/src/javax/media/j3d/SwitchState.java new file mode 100644 index 0000000..291a4bd --- /dev/null +++ b/src/javax/media/j3d/SwitchState.java @@ -0,0 +1,126 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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.err.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; in-1, where n + * 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + SwitchValueInterpolator svi = new SwitchValueInterpolator(); + svi.duplicateNode(this, forceDuplicate); + return svi; + } + + + /** + * Copies all SwitchValueInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/Targets.java b/src/javax/media/j3d/Targets.java new file mode 100644 index 0000000..d151756 --- /dev/null +++ b/src/javax/media/j3d/Targets.java @@ -0,0 +1,196 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 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 getTargetsData(int type, int index); +} diff --git a/src/javax/media/j3d/TexCoordGeneration.java b/src/javax/media/j3d/TexCoordGeneration.java new file mode 100644 index 0000000..fe9679a --- /dev/null +++ b/src/javax/media/j3d/TexCoordGeneration.java @@ -0,0 +1,686 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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. + *

+ * Texture coordinates consist of two, three or four coordinates. + * These coordinates + * are referred to as the S, T, R, and Q + * coordinates. + * 2D textures use the S and T coordinates. 3D textures + * use the S, T and R coordinates. The Q + * coordinate, similar to the w coordinate of the (x, y, z, w) + * object coordinates, is used to create homogeneous coordinates. + *

+ * 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: + *

    + *
  • Texture format - defines whether the generated texture + * coordinates are 2D, 3D, or 4D:

    + *

      + *
    • TEXTURE_COORDINATE_2 - generates 2D texture coordinates + * (S and T).

      + *

    • TEXTURE_COORDINATE_3 - generates 3D texture coordinates + * (S, T, and R).

      + *

    • TEXTURE_COORDINATE_4 - generates 4D texture coordinates + * (S, T, R, and Q).

      + *

    + *
  • Texture generation mode - defines how the texture coordinates + * are generated:

    + *

      + *
    • OBJECT_LINEAR - texture coordinates are generated as a linear + * function in object coordinates. The function used is:

      + *

        + * g = p1xo + p2yo + p3zo + p4wo + *

        + * where
        + *

          g is the value computed for the coordinate.
          + * p1, p2, + * p3, and p4 + * are the plane equation coefficients (described below).
          + * xo, yo, zo, and wo are + * the object coordinates of the vertex.

          + *

      + *
    • EYE_LINEAR - texture coordinates are generated as a linear + * function in eye coordinates. The function used is:

      + *

        + * g = p1'xe + p2'ye + p3'ze + p4'we + *

        + * where
        + *

          xe, ye, + * ze, and we are the eye + * coordinates of the vertex.
          + * p1', p2', + * p3', and p4' + * are the plane equation coefficients transformed into eye + * coordinates.

          + *

      + * + *
    • 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.

      + * + *

    • NORMAL_MAP - texture coordinates are generated to match + * vertices' normals in eye coordinates. This is only available if + * TextureCubeMap is available. + *
    • + * + *

    • REFLECTION_MAP - texture coordinates are generated to match + * vertices' reflection vectors in eye coordinates. This is only available + * if TextureCubeMap is available. + *
    • + *

    + *
  • 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. + *

    + * The equation coefficients are set by the setPlaneS, + * setPlaneT, setPlaneR, and setPlaneQ + * methods for each of the S, T, R, and Q coordinate functions, respectively. + * By default the equation coefficients are set as follows:

    + *

      + * plane S = (1.0, 0.0, 0.0, 0.0)
      + * plane T = (0.0, 1.0, 0.0, 0.0)
      + * plane R = (0.0, 0.0, 0.0, 0.0)
      + * plane Q = (0.0, 0.0, 0.0, 0.0)

      + *

+ * Texture coordinate generation is enabled or disabled by the + * setEnable 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.

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_ENABLE_READ, + ALLOW_FORMAT_READ, + ALLOW_MODE_READ, + ALLOW_PLANE_READ + }; + + /** + * Constructs a TexCoordGeneration object with default parameters. + * The default values are as follows: + *

    + * enable flag : true
    + * texture generation mode : OBJECT_LINEAR
    + * format : TEXTURE_COORDINATE_2
    + * plane S : (1,0,0,0)
    + * plane T : (0,1,0,0)
    + * plane R : (0,0,0,0)
    + * plane Q : (0,0,0,0)
    + *
+ */ + public TexCoordGeneration() { + // Just use the defaults + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 genMode is + * a value other than OBJECT_LINEAR, EYE_LINEAR, + * SPHERE_MAP, NORMAL_MAP, or + * REFLECTION_MAP. + * + * @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. + */ + @Override + void createRetained() { + this.retained = new TexCoordGenerationRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + TexCoordGeneration tga = new TexCoordGeneration(); + tga.duplicateNodeComponent(this); + return tga; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + + @Override + 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/javax/media/j3d/TexCoordGenerationRetained.java b/src/javax/media/j3d/TexCoordGenerationRetained.java new file mode 100644 index 0000000..aa704f5 --- /dev/null +++ b/src/javax/media/j3d/TexCoordGenerationRetained.java @@ -0,0 +1,416 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Vector4f; + +/** + * 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 + */ + @Override + 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); + } + } + + void updateNative(Canvas3D cv) { + int gMode = genMode; + Transform3D m = cv.vworldToEc; + + if (((cv.textureExtendedFeatures & Canvas3D.TEXTURE_CUBE_MAP) == 0) && + ((genMode == TexCoordGeneration.NORMAL_MAP) || + (genMode == TexCoordGeneration.REFLECTION_MAP))) { + gMode = TexCoordGeneration.SPHERE_MAP; + } + + Pipeline.getPipeline().updateTexCoordGeneration(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); + } + + /** + * Initializes a mirror object, point the mirror object to the retained + * object if the object is not editable + */ + @Override + synchronized void initMirrorObject() { + ((TexCoordGenerationRetained)mirror).set(this); + } + + /** Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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)); + } + + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + } + + @Override + 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/javax/media/j3d/Text3D.java b/src/javax/media/j3d/Text3D.java new file mode 100644 index 0000000..3791753 --- /dev/null +++ b/src/javax/media/j3d/Text3D.java @@ -0,0 +1,661 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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:

+ *

    + *
  • 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.
  • + *

  • Text string - the text string to be written.
  • + *

  • Position - determines the initial placement of the Text3D string + * in three-space.
  • + *

  • Alignment - specifies how glyphs in the string are placed in + * relation to the position parameter. Valid values are: + *
      + *
    • ALIGN_CENTER - the center of the string is placed on the + * position point.
    • + *
    • ALIGN_FIRST - the first character of the string is placed on + * the position point.
    • + *
    • ALIGN_LAST - the last character of the string is placed on the + * position point.
    • + *

    + *

  • Path - specifies how succeeding glyphs in the string are placed + * in relation to the previous glyph. Valid values are:
  • + *

      + *
    • PATH_LEFT - succeeding glyphs are placed to the left of the + * current glyph.
    • + *
    • PATH_RIGHT - succeeding glyphs are placed to the right of the + * current glyph.
    • + *
    • PATH_UP - succeeding glyphs are placed above the current glyph.
    • + *
    • PATH_DOWN - succeeding glyphs are placed below the current glyph.
    • + *

    + *

  • Character spacing - the space between characters. This spacing is + * in addition to the regular spacing between glyphs as defined in the + * Font object.

+ * + * @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; + + /** + * alignment: the center of the string is placed on the + * position point. + * + * @see #getAlignment + */ + public static final int ALIGN_CENTER = 0; + + /** + * alignment: the first character of the string is placed + * on the position point. + * + * @see #getAlignment + */ + public static final int ALIGN_FIRST = 1; + + /** + * alignment: the last character of the string is placed + * on the position point. + * + * @see #getAlignment + */ + public static final int ALIGN_LAST = 2; + + /** + * path: succeeding glyphs are placed to the left of + * the current glyph. + * + * @see #getPath + */ + public static final int PATH_LEFT = 0; + /** + * path: succeeding glyphs are placed to the left of + * the current glyph. + * + * @see #getPath + */ + public static final int PATH_RIGHT = 1; + + /** + * path: succeeding glyphs are placed above the + * current glyph. + * + * @see #getPath + */ + public static final int PATH_UP = 2; + + /** + * path: succeeding glyphs are placed below the + * current glyph. + * + * @see #getPath + */ + public static final int PATH_DOWN = 3; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_FONT3D_READ, + ALLOW_STRING_READ, + ALLOW_POSITION_READ, + ALLOW_ALIGNMENT_READ, + ALLOW_PATH_READ, + ALLOW_CHARACTER_SPACING_READ, + ALLOW_BOUNDING_BOX_READ + }; + + /** + * Constructs a Text3D object with default parameters. + * The default values are as follows: + *

    + * font 3D : null
    + * string : null
    + * position : (0,0,0)
    + * alignment : ALIGN_FIRST
    + * path : PATH_RIGHT
    + * character spacing : 0.0
    + *
+ */ + public Text3D() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Creates a Text3D object with the given Font3D object. + * + * @see Font3D + */ + public Text3D(Font3D font3D) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 position and succeeding + * glyphs are placed to the right of the initial glyph. + * + * @see Font3D + */ + public Text3D(Font3D font3D, String string, Point3f position) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 position position depends on + * the alignment parameter and the path parameter. + * + * @see Font3D + */ + public Text3D(Font3D font3D, String string, Point3f position, + int alignment, int path) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 position field into the supplied + * parameter. The position 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 position field to the supplied + * parameter. The position 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 alignment is used to specify how + * glyphs in the string are placed in relation to the + * position field. Valid values for this field + * are: + *
    + *
  • ALIGN_CENTER - the center of the string is placed on the + * position point. + *
  • ALIGN_FIRST - the first character of the string is placed on + * the position point. + *
  • ALIGN_LAST - the last character of the string is placed on the + * position point. + *
+ * The default value of this field is ALIGN_FIRST. + * + * @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 alignment is used to specify how + * glyphs in the string are placed in relation to the + * position field. Valid values for this field + * are: + *
    + *
  • ALIGN_CENTER - the center of the string is placed on the + * position point. + *
  • ALIGN_FIRST - the first character of the string is placed on + * the position point. + *
  • ALIGN_LAST - the last character of the string is placed on the + * position point. + *
+ * The default value of this field is ALIGN_FIRST. + * + * @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 path 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: + *
    + *
  • PATH_LEFT: - succeeding glyphs are placed to the left of the + * current glyph. + *
  • PATH_RIGHT: - succeeding glyphs are placed to the right of the + * current glyph. + *
  • PATH_UP: - succeeding glyphs are placed above the current glyph. + *
  • PATH_DOWN: - succeeding glyphs are placed below the current glyph. + *
+ * The default value of this field is PATH_RIGHT. + * + * @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 path 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: + *
    + *
  • PATH_LEFT - succeeding glyphs are placed to the left of the + * current glyph. + *
  • PATH_RIGHT - succeeding glyphs are placed to the right of the + * current glyph. + *
  • PATH_UP - succeeding glyphs are placed above the current glyph. + *
  • PATH_DOWN - succeeding glyphs are placed below the current glyph. + *
+ * The default value of this field is PATH_RIGHT. + * + * @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) + */ + @Override + public NodeComponent cloneNodeComponent() { + Text3D t = new Text3D(); + t.duplicateNodeComponent(this); + return t; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/Text3DRenderMethod.java b/src/javax/media/j3d/Text3DRenderMethod.java new file mode 100644 index 0000000..6b2d2dc --- /dev/null +++ b/src/javax/media/j3d/Text3DRenderMethod.java @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + */ + @Override + public boolean render(RenderMolecule rm, Canvas3D cv, + 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(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, + cv.screen.screen, + rm.textureBin.attributeBin.ignoreVertexColors); + 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(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, + cv.screen.screen, + rm.textureBin.attributeBin. + ignoreVertexColors); + isVisible = true; + } + } + else { + if (!VirtualUniverse.mc.viewFrustumCulling || + ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(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, + cv.screen.screen, + rm.textureBin.attributeBin. + ignoreVertexColors); + isVisible = true; + } + else { + cv.raIsVisible = false; + } + cv.ra = ra.renderAtom; + } + + ra = ra.next; + + } + return isVisible; + } +} diff --git a/src/javax/media/j3d/Text3DRetained.java b/src/javax/media/j3d/Text3DRetained.java new file mode 100644 index 0000000..ed21941 --- /dev/null +++ b/src/javax/media/j3d/Text3DRetained.java @@ -0,0 +1,999 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.font.GlyphVector; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; + +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; + +/** + * 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; + } + + + @Override + 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; iposition field into the supplied + * parameter. The position 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 position field to the supplied + * parameter. The position 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 alignment is used to specify how + * glyphs in the string are placed in relation to the + * position field. Valid values for this field + * are: + *
    + *
  • ALIGN_CENTER - the center of the string is placed on the + * position point. + *
  • ALIGN_FIRST - the first character of the string is placed on + * the position point. + *
  • ALIGN_LAST - the last character of the string is placed on the + * position point. + *
+ * The default value of this field is ALIGN_FIRST. + * + * @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 alignment is used to specify how + * glyphs in the string are placed in relation to the + * position field. Valid values for this field + * are: + *
    + *
  • ALIGN_CENTER - the center of the string is placed on the + * position point. + *
  • ALIGN_FIRST - the first character of the string is placed on + * the position point. + *
  • ALIGN_LAST - the last character of the string is placed on the + * position point. + *
+ * The default value of this field is ALIGN_FIRST. + * + * @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 path 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: + *
    + *
  • PATH_LEFT: - succeeding glyphs are placed to the left of the + * current glyph. + *
  • PATH_RIGHT: - succeeding glyphs are placed to the right of the + * current glyph. + *
  • PATH_UP: - succeeding glyphs are placed above the current glyph. + *
  • PATH_DOWN: - succeeding glyphs are placed below the current glyph. + *
+ * The default value of this field is PATH_RIGHT. + * + * @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 path 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: + *
    + *
  • PATH_LEFT - succeeding glyphs are placed to the left of the + * current glyph. + *
  • PATH_RIGHT - succeeding glyphs are placed to the right of the + * current glyph. + *
  • PATH_UP - succeeding glyphs are placed above the current glyph. + *
  • PATH_DOWN - succeeding glyphs are placed below the current glyph. + *
+ * The default value of this field is PATH_RIGHT. + * + * @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 gaList; + 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 shapeList = userLists.get(i); + newGeometryAtomList.clear(); + oldGeometryAtomList.clear(); + + for (j=0; j 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 gaList = new ArrayList(); + GeometryRetained geomR; + synchronized(liveStateLock) { + if (source.isLive()) { + synchronized (universeList) { + numMessages = universeList.size(); + m = new J3dMessage[numMessages]; + for (i=0; i shapeList = userLists.get(i); + // gaList = new GeometryAtom[shapeList.size() * numChars]; + for (j = 0; j < shapeList.size(); j++) { + Shape3DRetained s = shapeList.get(j); + + // Find the right geometry. + for(sCnt=0; sCnt= 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.set(closestIPnt); + charTransforms[sIndex].transform(iPnt); + return true; + } + return false; + } + + @Override + boolean intersect(Point3d[] pnts) { + Transform3D tempT3D = new Transform3D(); + 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; + } + } + } + return isIntersect; + } + + + @Override + 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; + } + + @Override + 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; + } + + + @Override + void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, + boolean updateAlpha, float alpha, + int screen, + boolean ignoreVertexColors) { + + Transform3D trans = new Transform3D(); + + 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, + screen, ignoreVertexColors); + } + } + + @Override + 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 + * Each Texture object has the following properties:

+ *

    + *
  • 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.
  • + *

  • Boundary Width - the texture boundary width, which must be 0 or 1. + * If the texture boundary + * width is 1, then all images for all mipmap levels will include a border. + * 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. + *
  • + *

  • Boundary ModeS and Boundary ModeT - the boundary mode for the + * S and T coordinates, respectively. The boundary modes are as + * follows:
  • + *

      + *
    • 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.
    • + *

    • 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
    • + *

    • 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.
    • + *

    • 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.
    • + *
    + *
  • 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.
  • + *

  • 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:
  • + *

      + *
    • FASTEST - uses the fastest available method for processing + * geometry.
    • + *

    • NICEST - uses the nicest available method for processing + * geometry.
    • + *

    • BASE_LEVEL_POINT - selects the nearest texel in the base level + * texture image.
    • + *

    • 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:
    • + *

        + * i0 = trunc(u - 0.5)

        + * j0 = trunc(v - 0.5)

        + * i1 = i0 + 1

        + * j1 = j0 + 1

        + * a = frac(u - 0.5)

        + * b = frac(v - 0.5)

        + * T' = (1-a)*(1-b)*Ti0j0 + + * a*(1-b)*Ti1j0 + + * (1-a)*b*Ti0j1 + + * a*b*Ti1j1

        + *

      + *
    • LINEAR_SHARPEN - sharpens the resulting image by extrapolating + * from the base level plus one image to the base level image of this + * texture object.
    • + *

    • LINEAR_SHARPEN_RGB - performs linear sharpen filter for the rgb + * components only. The alpha component is computed using BASE_LEVEL_LINEAR + * filter.
    • + *

    • LINEAR_SHARPEN_ALPHA - performs linear sharpen filter for the alpha + * component only. The rgb components are computed using BASE_LEVEL_LINEAR + * filter.
    • + *

    • 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:
    • + *

        + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
        i1 = trunc(u - 0.5)i2 = i1 + 1i3 = i2 + 1i0 = i1 - 1
        j1 = trunc(v - 0.5)j3 = j2 + 1j2 = j1 + 1j0 = j1 - 1
        a = frac(u - 0.5)
        b = frac(v - 0.5)
        + * f(x) : filter4 function where 0<=x<=2

        + * T' = f(1+a) * f(1+b) * Ti0j0 + + * f(a) * f(1+b) * Ti1j0 + + * f(1-a) * f(1+b) * Ti2j0 + + * f(2-a) * f(1+b) * Ti3j0 +
        + * f(1+a) * f(b) * Ti0j1 + + * f(a) * f(b) * Ti1j1 + + * f(1-a) * f(b) * Ti2j1 + + * f(2-a) * f(b) * Ti3j1 +
        + * f(1+a) * f(1-b) * Ti0j2 + + * f(a) * f(1-b) * Ti1j2 + + * f(1-a) * f(1-b) * Ti2j2 + + * f(2-a) * f(1-b) * Ti3j2 +
        + * f(1+a) * f(2-b) * Ti0j3 + + * f(a) * f(2-b) * Ti1j3 + + * f(1-a) * f(2-b) * Ti2j3 + + * f(2-a) * f(2-b) * Ti3j3

        + *

      + *
    + *
  • 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:
  • + *

      + *
    • FASTEST - uses the fastest available method for processing + * geometry.
    • + *

    • NICEST - uses the nicest available method for processing + * geometry.
    • + *

    • BASE_LEVEL_POINT - selects the nearest level in the base level + * texture map.
    • + *

    • BASE_LEVEL_LINEAR - performs a bilinear interpolation on the four + * nearest texels in the base level texture map.
    • + *

    • MULTI_LEVEL_POINT - selects the nearest texel in the nearest + * mipmap.
    • + *

    • MULTI_LEVEL_LINEAR - performs trilinear interpolation of texels + * between four texels each from the two nearest mipmap levels.
    • + *

    • FILTER4 - applies an application-supplied weight function + * on the nearest 4x4 texels in the base level texture image.
    • + *

    + *
  • MIPmap mode - the mode used for texture mapping for this + * object. The mode is one of the following:
  • + *

      + *
    • BASE_LEVEL - indicates that this Texture object only has a + * base-level image. If multiple levels are needed, they will be + * implicitly computed.
    • + *

    • 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.
    • + *

    + *
  • Format - the data format. The format is one of the + * following:
  • + *

      + *
    • INTENSITY - the texture image contains only texture + * values.
    • + *

    • LUMINANCE - the texture image contains only + * luminance values.
    • + *

    • ALPHA - the texture image contains only alpha + * values.
    • + *

    • LUMINANCE_ALPHA - the texture image contains + * both luminance and alpha values.
    • + *

    • RGB - the texture image contains red, green, + * and blue values.
    • + *

    • RGBA - the texture image contains red, green, blue, and alpha + * values.
    + *
  • Base Level - specifies the mipmap level to be used when filter + * specifies BASE_LEVEL_POINT or BASE_LEVEL_LINEAR.
  • + *

  • 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.
  • + *

  • Minimum LOD - specifies the minimum of the LOD range. LOD smaller + * than this value will be clamped to this value.
  • + *

  • Maximum LOD - specifies the maximum of the LOD range. LOD larger + * than this value will be clamped to this value.
  • + *

  • LOD offset - specifies the offset to be used in the LOD calculation + * to compensate for under or over sampled texture images.
  • + *
  • Anisotropic Mode - defines how anisotropic filter is applied for + * this texture object. The anisotropic modes are as follows:
  • + *

      + *
    • ANISOTROPIC_NONE - no anisotropic filtering.
    • + *

    • ANISOTROPIC_SINGLE_VALUE - applies the degree of anisotropic filter + * in both the minification and magnification filters.
    • + *

    + *
  • 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.
  • + *

  • 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:
  • + *

      + * T' = ((1 + SharpenFunc(LOD)) * TBaseLevel) - (SharpenFunc(LOD) * TBaseLevel+1)

      + *

    + *
  • 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 2m + 1 for some integer value of m + * greater than or equal to 4.
  • + *

+ * + *

+ * Note that as of Java 3D 1.5, the texture width and height are no longer + * required to be an exact power of two. However, not all graphics devices + * supports non-power-of-two textures. If non-power-of-two texture mapping is + * unsupported on a particular Canvas3D, textures with a width or height that + * are not an exact power of two are ignored for that canvas. + * + * @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.

+ * + * @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 + * log2(max(width,height))+1 + * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_ANISOTROPIC_FILTER_READ, + ALLOW_BOUNDARY_COLOR_READ, + ALLOW_BOUNDARY_MODE_READ, + ALLOW_ENABLE_READ, + ALLOW_FILTER4_READ, + ALLOW_FILTER_READ, + ALLOW_FORMAT_READ, + ALLOW_IMAGE_READ, + ALLOW_LOD_RANGE_READ, + ALLOW_MIPMAP_MODE_READ, + ALLOW_SHARPEN_TEXTURE_READ, + ALLOW_SIZE_READ + }; + + /** + * Constructs a Texture object with default parameters. + * The default values are as follows: + *
    + * enable flag : true
    + * width : 0
    + * height : 0
    + * mipmap mode : BASE_LEVEL
    + * format : RGB
    + * boundary mode S : WRAP
    + * boundary mode T : WRAP
    + * min filter : BASE_LEVEL_POINT
    + * mag filter : BASE_LEVEL_POINT
    + * boundary color : black (0,0,0,0)
    + * boundary width : 0
    + * array of images : null
    + * baseLevel : 0
    + * maximumLevel : log2(max(width,height))
    + * minimumLOD : -1000.0
    + * maximumLOD : 1000.0
    + * lod offset : (0, 0, 0)
    + * anisotropic mode : ANISOTROPIC_NONE
    + * anisotropic filter : 1.0
    + * sharpen texture func: null
    + * filter4 func: null
    + *
+ *

+ * 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 + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs an empty Texture object with specified mipMapMode, + * format, width and height. Defaults are used for all other + * parameters. If mipMapMode is set to + * BASE_LEVEL, then the image at level 0 must be set + * by the application (using either the setImage or + * setImages method). If mipMapMode is + * set to MULTI_LEVEL_MIPMAP, then images for levels + * Base Level through Maximum Level must be set. + * Note that a texture with a non-power-of-two width or height will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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. + * @param height height of image at level 0. + * @exception IllegalArgumentException if width or height are not greater + * than 0, or if an invalid format or mipMapMode is specified. + */ + public Texture(int mipMapMode, + int format, + int width, + int height) { + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + 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")); + } + + if (width < 1) { + throw new IllegalArgumentException(J3dI18N.getString("Texture46")); + } + + if (height < 1) { + throw new IllegalArgumentException(J3dI18N.getString("Texture47")); + } + + int widthLevels; + int heightLevels; + + widthLevels = getLevelsNPOT(width); + heightLevels = getLevelsNPOT(height); + + ((TextureRetained)this.retained).initialize(format, width, widthLevels, + height, heightLevels, mipMapMode, 0); + } + + /** + * Constructs an empty Texture object with specified mipMapMode, + * format, width, height, and boundaryWidth. + * Defaults are used for all other + * parameters. If mipMapMode is set to + * BASE_LEVEL, then the image at level 0 must be set + * by the application (using either the setImage or + * setImages method). If mipMapMode is + * set to MULTI_LEVEL_MIPMAP, then images for levels + * Base Level through Maximum Level must be set. + * Note that a texture with a non-power-of-two width or height will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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. This + * does not include the width of the boundary. + * @param height height of image at level 0. This + * does not include the width of the boundary. + * @param boundaryWidth width of the boundary, which must be 0 or 1. + * @exception IllegalArgumentException if width or height are not greater + * than 0, if an invalid format or mipMapMode is specified, or + * if the boundaryWidth is < 0 or > 1 + * + * @since Java 3D 1.3 + */ + public Texture(int mipMapMode, + int format, + int width, + int height, + int boundaryWidth) { + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + 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")); + } + + if (width < 1) { + throw new IllegalArgumentException(J3dI18N.getString("Texture46")); + } + + if (height < 1) { + throw new IllegalArgumentException(J3dI18N.getString("Texture47")); + } + + int widthLevels; + int heightLevels; + + widthLevels = getLevelsNPOT(width); + heightLevels = getLevelsNPOT(height); + + if (boundaryWidth < 0 || boundaryWidth > 1) + throw new IllegalArgumentException(J3dI18N.getString("Texture30")); + + ((TextureRetained)this.retained).initialize(format, width, widthLevels, + height, heightLevels, 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 boundaryModeS + * is a value other than CLAMP, WRAP, + * CLAMP_TO_EDGE, or CLAMP_TO_BOUNDARY. + */ + 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 boundaryModeT + * is a value other than CLAMP, WRAP, + * CLAMP_TO_EDGE, or CLAMP_TO_BOUNDARY. + */ + 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 minFilter + * is a value other than FASTEST, NICEST, + * BASE_LEVEL_POINT, BASE_LEVEL_LINEAR, + * MULTI_LEVEL_POINT, MULTI_LEVEL_LINEAR, or + * FILTER4. + * + * @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 magFilter + * is a value other than FASTEST, NICEST, + * BASE_LEVEL_POINT, BASE_LEVEL_LINEAR, + * LINEAR_SHARPEN, LINEAR_SHARPEN_RGB, + * LINEAR_SHARPEN_ALPHA, or + * FILTER4. + * + * @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. Note that the image size + * must be the correct size for the specified mipmap level. The image size + * of the base level image, that is level 0, must be the same size + * in each dimension (width, height, depth) as this + * texture, excluding the border, if any. + * Each successive mipmap level must be 1/2 the size of the previous level, + * such that size[n] = floor(size[n-1]/2), exluding + * the border. + * + * @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 or if an ImageComponent2D is used in a + * Texture3D object. + * + * @exception IllegalArgumentException if the image being set at this + * level is not the correct size for this level. + * + * @exception IllegalSharingException if this Texture is live and + * the specified image is being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this Texture is + * being used by an immediate mode context and + * the specified image is being used by a Canvas3D as an off-screen buffer. + */ + public void setImage(int level, ImageComponent image) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_IMAGE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("Texture15")); + } + + // Do illegal sharing check + validateImageIllegalSharing(image); + + 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. Note that the image size + * of the base level image, images[0], must be the same size + * in each dimension (width, height, depth) as this + * texture, excluding the border, if any. + * Each successive mipmap level must be 1/2 the size of the previous level, + * such that size[n] = floor(size[n-1]/2), exluding + * the border. + * + * @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 or if an ImageComponent2D is used in a + * Texture3D object. + * + * @exception IllegalArgumentException if images.length is + * not equal to the total number of mipmap levels. + * + * @exception IllegalArgumentException if the size of each dimension + * of the image at a given level in the + * images array is not the correct size. + * + * @exception IllegalSharingException if this Texture is live and + * any of the specified images are being used by a Canvas3D as an + * off-screen buffer. + * + * @exception IllegalSharingException if this Texture is + * being used by an immediate mode context and + * any of the specified images are being used by a Canvas3D as an + * off-screen buffer. + * + * @since Java 3D 1.2 + */ + public void setImages(ImageComponent[] images) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_IMAGE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("Texture15")); + } + + // Do illegal sharing check + for(int i=0; imipMapMode is + * MULTI_LEVEL_MIPMAP; 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 mipMapMode + * is a value other than BASE_LEVEL or + * MULTI_LEVEL_MIPMAP. + */ + 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; + } + + // returns number of levels using NPOT rules for mipmap generation + // which say that each level should be floor(size/2) of previous level + static int getLevelsNPOT(int num) { + int tmp, levels = 0; + tmp = num; + while (tmp > 1) { + tmp = tmp / 2; + levels++; + } + return levels; + } + + /** + * 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 > log2(max(width,height)) + * @exception IllegalArgumentException if mipMipMapMode is equal to BASE_LEVEL + * and maximumLevel is not equal to zero. + * + * @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 + * mode is a value other than + * ANISOTROPIC_NONE or ANISOTROPIC_SINGLE_VALUE + * + * @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 + * degree < 1.0 or + * degree > 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 lod + * does not match the length of pts + * @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 + * weight < 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 originalNodeComponent + * into the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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); + } + } + // XXXX: clone new v1.2 attributes? + // NOTE: This sppears to have already been done + } + + /** + * 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. + */ + @Override + 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/javax/media/j3d/Texture2D.java b/src/javax/media/j3d/Texture2D.java new file mode 100644 index 0000000..6f32db6 --- /dev/null +++ b/src/javax/media/j3d/Texture2D.java @@ -0,0 +1,572 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point2f; + + +/** + * 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. + *

+ * Note that as of Java 3D 1.5, the texture width and height are no longer + * required to be an exact power of two. However, not all graphics devices + * supports non-power-of-two textures. If non-power-of-two texture mapping is + * unsupported on a particular Canvas3D, textures with a width or height that + * are not an exact power of two are ignored for that canvas. + * + * @see Canvas3D#queryProperties + */ +public class Texture2D extends Texture { + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * 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; + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * 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; + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * 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; + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * 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; + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * 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; + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_DETAIL_TEXTURE_READ + }; + + /** + * Constructs a texture object using default values. + * + * The default values are as follows: + *

    + * detail texture image: null
    + * detail texture mode: DETAIL_MODULATE
    + * detail texture func: null
    + * detail texture level: 2
    + *
+ *

+ * Note that the default constructor creates a texture object with + * a width and height of 0 and is, therefore, not useful. + */ + public Texture2D() { + super(); + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + } + + /** + * 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. + * Note that a texture with a non-power-of-two width or height will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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. + * @param height height of image at level 0. + * @exception IllegalArgumentException if width or height are NOT + * greater than 0 OR invalid format/mipmapMode is specified. + */ + public Texture2D( + int mipMapMode, + int format, + int width, + int height) { + + super(mipMapMode, format, width, height); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + + /** + * Constructs an empty Texture2D object with specified mipMapMode, + * format, width, height, and boundaryWidth. + * Defaults are used for all other + * parameters. If mipMapMode is set to + * BASE_LEVEL, then the image at level 0 must be set + * by the application (using either the setImage or + * setImages method). If mipMapMode is + * set to MULTI_LEVEL_MIPMAP, then images for levels + * Base Level through Maximum Level must be set. + * Note that a texture with a non-power-of-two width or height will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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. This + * does not include the width of the boundary. + * @param height height of image at level 0. This + * does not include the width of the boundary. + * @param boundaryWidth width of the boundary, which must be 0 or 1. + * @exception IllegalArgumentException if width or height are not greater + * than 0, if an invalid format or mipMapMode is specified, or + * if the boundaryWidth is < 0 or > 1 + * + * @since Java 3D 1.3 + */ + public Texture2D(int mipMapMode, + int format, + int width, + int height, + int boundaryWidth) { + + super(mipMapMode, format, width, height, boundaryWidth); + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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 minFilter + * is a value other than 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. + * + * @see Canvas3D#queryProperties + * + * @since Java 3D 1.3 + */ + @Override + 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); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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(); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @param mode detail texture mode. One of: DETAIL_ADD or DETAIL_MODULATE + * + * @exception IllegalArgumentException if + * mode is a value other than + * DETAIL_ADD, or DETAIL_MODULATE + * @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); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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(); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @param level the detail texture level. + * + * @exception IllegalArgumentException if level < 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); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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(); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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 lod + * does not match the length of pts + * @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")); + } + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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(); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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); + } + + /** + * @deprecated As of Java 3D 1.5 the optional detail texture feature is no + * longer supported. + * + * @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. + */ + @Override + void createRetained() { + this.retained = new Texture2DRetained(); + this.retained.setSource(this); + } + + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + 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 not call this method directly. + * It should only be called by the cloneNode method. + * + * @deprecated replaced with duplicateNodeComponent( + * NodeComponent originalNodeComponent, boolean forceDuplicate) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/Texture2DRetained.java b/src/javax/media/j3d/Texture2DRetained.java new file mode 100644 index 0000000..ce6803d --- /dev/null +++ b/src/javax/media/j3d/Texture2DRetained.java @@ -0,0 +1,202 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point2f; + +/** + * 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 { + + // Note : There is hardly any HW vendor supports detail Image. + // Detail Image operation is simply no-op in 1.5. + + // currently detail image is only applicable to 2D texture + // detail texture info + + // These members are unused except for public set and get methods. + private ImageComponent2DRetained detailImage = null; + private int detailTextureMode = Texture2D.DETAIL_MODULATE; + private int detailTextureLevel = 2; + private int numDetailTextureFuncPts = 0; + private 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; + } + } + + + /** + * 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; + } + + +} + diff --git a/src/javax/media/j3d/Texture3D.java b/src/javax/media/j3d/Texture3D.java new file mode 100644 index 0000000..6b2f955 --- /dev/null +++ b/src/javax/media/j3d/Texture3D.java @@ -0,0 +1,266 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * Note that as of Java 3D 1.5, the texture width, height, and depth + * are no longer + * required to be an exact power of two. However, not all graphics devices + * supports non-power-of-two textures. If non-power-of-two texture mapping is + * unsupported on a particular Canvas3D, textures with a width, height, + * or depth that are not an exact power of two are ignored for that canvas. + * + * @see Canvas3D#queryProperties + */ + +public class Texture3D extends Texture { + + // TODO KCR : NPOT + + /** + * Constructs a Texture3D object with default parameters. + * The default values are as follows: + *

    + * depth : 0
    + * boundary mode R : WRAP
    + *
+ *

+ * 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. + * Note that a texture with a non-power-of-two width, height, or depth will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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. + * @param height height of image at level 0. + * @param depth depth of image at level 0. + * @exception IllegalArgumentException if width, height, or depth are not + * greater than 0 OR invalid format/mipmapMode is specified. + */ + public Texture3D(int mipmapMode, + int format, + int width, + int height, + int depth) { + + super(mipmapMode, format, width, height); + + int depthLevels = -1; + + depthLevels = getLevelsNPOT(depth); + + // TODO : Need to verify whether this is a bug. Why depthLevels isn't + // use to determine maxMipMapLevels ? See also Texture.java + + ((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. + * Note that a texture with a non-power-of-two width, height, or depth will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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. This + * does not include the width of the boundary. + * @param height height of image at level 0. This + * does not include the width of the boundary. + * @param depth depth of image at level 0. This + * does not include the width of the boundary. + * @param boundaryWidth width of the boundary, which must be 0 or 1. + * @exception IllegalArgumentException if width, height, or depth are not + * greater than 0 OR invalid format/mipmapMode is specified, or + * if the boundaryWidth is < 0 or > 1 + * + * @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 depthLevels = -1; + + depthLevels = getLevelsNPOT(depth); + + // TODO : Need to verify whether this is a bug. Why depthLevels isn't + // use to determine maxMipMapLevels ? See also Texture.java + + ((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 boundaryModeR + * is a value other than CLAMP, WRAP, + * CLAMP_TO_EDGE, or CLAMP_TO_BOUNDARY. + */ + 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. + */ + @Override + void createRetained() { + this.retained = new Texture3DRetained(); + this.retained.setSource(this); + } + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + 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 not call this method directly. + * It should only be called by the cloneNode method. + * + * @deprecated replaced with duplicateNodeComponent( + * NodeComponent originalNodeComponent, boolean forceDuplicate) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + void duplicateAttributes(NodeComponent originalNodeComponent, + boolean forceDuplicate) { + super.duplicateAttributes(originalNodeComponent, forceDuplicate); + + ((Texture3DRetained) retained).initBoundaryModeR(((Texture3DRetained) + originalNodeComponent.retained).getBoundaryModeR()); + + } + +} diff --git a/src/javax/media/j3d/Texture3DRetained.java b/src/javax/media/j3d/Texture3DRetained.java new file mode 100644 index 0000000..c216c08 --- /dev/null +++ b/src/javax/media/j3d/Texture3DRetained.java @@ -0,0 +1,240 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * 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. + */ + @Override + void bindTexture(Context ctx, int objectId, boolean enable) { + Pipeline.getPipeline().bindTexture3D(ctx, objectId, enable); + } + + void updateTextureBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + int boundaryModeR, float boundaryRed, + float boundaryGreen, float boundaryBlue, + float boundaryAlpha) { + + Pipeline.getPipeline().updateTexture3DBoundary(ctx, + boundaryModeS, boundaryModeT, boundaryModeR, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + @Override + void updateTextureFilterModes(Context ctx, + int minFilter, int magFilter) { + + Pipeline.getPipeline().updateTexture3DFilterModes(ctx, + minFilter, magFilter); + } + + @Override + void updateTextureSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + + Pipeline.getPipeline().updateTexture3DSharpenFunc(ctx, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + @Override + void updateTextureFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + + Pipeline.getPipeline().updateTexture3DFilter4Func(ctx, + numFilter4FuncPts, filter4FuncPts); + } + + @Override + void updateTextureAnisotropicFilter(Context ctx, float degree) { + Pipeline.getPipeline().updateTexture3DAnisotropicFilter(ctx, degree); + } + + + + // Wrapper around the native call for 3D textures + void updateTextureImage(Canvas3D cv, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, int depth, + int boundaryWidth, int imageDataType, + Object imageData) { + + Pipeline.getPipeline().updateTexture3DImage(cv.ctx, + numLevels, level, + textureFormat, imageFormat, + width, height, depth, + boundaryWidth, imageDataType, imageData, useAutoMipMapGeneration(cv)); + } + + // Wrapper around the native call for 3D textures + void updateTextureSubImage(Canvas3D cv, + int face, int level, + int xoffset, int yoffset, int zoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, int imgZOffset, + int tilew, int tileh, int width, int height, int depth, + int imageDataType, Object imageData) { + + Pipeline.getPipeline().updateTexture3DSubImage(cv.ctx, + level, xoffset, yoffset, zoffset, + textureFormat, imageFormat, + imgXOffset, imgYOffset, imgZOffset, + tilew, tileh, width, height, depth, + imageDataType, imageData, useAutoMipMapGeneration(cv)); + } + + // load level 0 image with null data pointer, just to enable + // mipmapping when level 0 is not the base level + + @Override + void updateTextureDimensions(Canvas3D cv) { + if(images[0][0] != null) { + updateTextureImage(cv, maxLevels, 0, 0, + format, images[0][0].getImageFormatTypeIntValue(false), + width, height, depth, boundaryWidth, + images[0][0].getImageDataTypeIntValue(), null); + } + } + + + @Override + void updateTextureBoundary(Canvas3D cv) { + updateTextureBoundary(cv.ctx, + boundaryModeS, boundaryModeT, boundaryModeR, + boundaryColor.x, boundaryColor.y, + boundaryColor.z, boundaryColor.w); + } + + @Override + void updateTextureLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + + Pipeline.getPipeline().updateTexture3DLodRange(ctx, baseLevel, maximumLevel, + minimumLod, maximumLod); + } + + @Override + void updateTextureLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + + Pipeline.getPipeline().updateTexture3DLodOffset(ctx, + lodOffsetX, lodOffsetY, lodOffsetZ); + } + + @Override + void reloadTextureImage(Canvas3D cv, int face, int level, + ImageComponentRetained image, int numLevels) { + +/* + System.err.println("Texture3D.reloadTextureImage: level= " + level + + " image.imageYup= " + image.imageYup + " w= " + image.width + + " h= " + image.height + " d= " + depth + + " numLevels= " + numLevels); +*/ + + // Texture3D does not need to support Raster + ImageComponentRetained.ImageData imageData = image.getImageData(false); + + updateTextureImage(cv, + 0, numLevels, level, format, + image.getImageFormatTypeIntValue(false), + image.width, image.height, depth, + boundaryWidth, image.getImageDataTypeIntValue(), + imageData.get()); + } + + @Override + 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; + int yoffset = y; + // Texture3D does not need to support Raster + ImageComponentRetained.ImageData imageData = image.getImageData(false); + + updateTextureSubImage(cv, + 0, level, xoffset, yoffset, z, + format, image.getImageFormatTypeIntValue(false), + xoffset, yoffset, z, + image.width, image.height, + width, height, 1, image.getImageDataTypeIntValue(), + imageData.get()); + } + +} diff --git a/src/javax/media/j3d/TextureAttributes.java b/src/javax/media/j3d/TextureAttributes.java new file mode 100644 index 0000000..28e1bfc --- /dev/null +++ b/src/javax/media/j3d/TextureAttributes.java @@ -0,0 +1,1452 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Color4f; + +/** + * The TextureAttributes object defines attributes that apply to + * texture mapping. + * The texture attributes include the following:

+ *

    + *
  • Texture mode - defines how the object and texture colors + * are blended. The mode may be one of the following:
  • + *

      + *
    • MODULATE - modulates the incoming color with the texture + * color.

      + *

        + * C' = C Ct + *
    • + *

    • DECAL - applies the texture color to the incoming color as a decal.

      + *

        + * C'rgb = Crgb (1 - Cta) + Ctrgb Cta

        + * C'a = Ca + *

    • + *

    • BLEND - blends the texture blend color with the incoming color.

      + *

        + * C'rgb = Crgb (1 - Ctrgb) + Cbrgb Ctrgb

        + * C'a = Ca Cta

        + *

      + * Note that if the texture format is INTENSITY, alpha is computed identically + * to red, green, and blue:

      + *

        + * C'a = Ca (1 - Cta) + Cba Cta + *
    • + *

    • REPLACE - replaces the incoming color with the texture color.

      + *

        + * C' = Ct

        + *

    • + *

    • 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.
    • + *

      + *

    + * C = Incoming color to the texture unit state. For texture unit state 0, C is the object color + * Ct = Texture color
    + * Cb = Texture blend color
    + *

    + *

  • Combine Mode - defines the combine operation when texture mode + * specifies COMBINE. The combine mode includes the following:

    + *

      + *
    • COMBINE_REPLACE

      + *

        + * C' = C0

        + *

    • + *

    • COMBINE_MODULATE

      + *

        + * C' = C0 C1 + *
    • + *

    • COMBINE_ADD

      + *

        + * C' = C0 + C1

        + *

    • + *

    • COMBINE_ADD_SIGNED

      + *

        + * C' = C0 + C1 - 0.5

        + *

    • + *

    • COMBINE_SUBTRACT

      + *

        + * C' = C0 - C1

        + *

    • + *

    • COMBINE_INTERPOLATE

      + *

        + * C' = C0 C2 + C1 (1 - C2)

        + *

    • + *

    • COMBINE_DOT3

      + *

        + * C' = 4 * ( + * (C0r - 0.5) * (C1r - 0.5) + + * (C0g - 0.5) * (C1g - 0.5) + + * (C0b - 0.5) * (C1b - 0.5))

        + * where CNx is the x component of the Nth color operand + * in the combine operation.

        + * The value C' will be placed to the all three r,g,b components or the + * a component of the output. + *

    • + *

  • + * where C0, C1 and C2 are determined by + * the color source, and the color operand. + *

+ *

    + *
  • Combine Color Source - defines the source for a color operand in the + * combine operation. The color source includes the following:

    + *

      + *
    • COMBINE_OBJECT_COLOR - object color

      + *

    • COMBINE_TEXTURE_COLOR - texture color

      + *

    • COMBINE_CONSTANT_COLOR - texture blend color

      + *

    • COMBINE_PREVIOUS_TEXTURE_UNIT_STATE - color from the previous texture + * unit state. For texture unit state 0, this is equivalent to + * COMBINE_OBJECT_COLOR.

      + *

  • + *

  • Combine Color Function - specifies the function for a color operand + * in the combine operation. The valid values are:

    + *

      + *
    • COMBINE_SRC_COLOR - the color function is f = Crgb

      + *

    • COMBINE_ONE_MINUS_SRC_COLOR - the color function is f = (1 - Crgb)

      + *

    • COMBINE_SRC_ALPHA - the color function is f = Ca

      + *

    • COMBINE_ONE_MINUS_SRC_ALPHA - the color function is f = (1 - Ca)

      + *

  • + *

  • 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.
  • + *

  • 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.
  • + *

  • Blend color - the constant texture blend color
  • + *

  • Perspective correction - the perspective correction mode + * used for color and texture coordinate interpolation. One of + * the following:
  • + *

      + *
    • NICEST - uses the nicest (highest quality) available + * method for texture mapping perspective correction.
    • + *

    • FASTEST - uses the fastest available method for texture + * mapping perspective correction.
    • + *

    + *
  • Texture color table - defines a table that is used to look up + * texture colors before applying the texture mode.
  • + *
+ * + * @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 = Crgb + * + * @since Java 3D 1.3 + * @see #setCombineRgbFunction + */ + public static final int COMBINE_SRC_COLOR = 0; + + /** + * Color function is f = (1 - Crgb) + * + * @since Java 3D 1.3 + * @see #setCombineRgbFunction + */ + public static final int COMBINE_ONE_MINUS_SRC_COLOR = 1; + + /** + * Color function is f = Ca + * + * @since Java 3D 1.3 + * @see #setCombineRgbFunction + * @see #setCombineAlphaFunction + */ + public static final int COMBINE_SRC_ALPHA = 2; + + /** + * Color function is f = (1 - Ca) + * + * @since Java 3D 1.3 + * @see #setCombineRgbFunction + * @see #setCombineAlphaFunction + */ + public static final int COMBINE_ONE_MINUS_SRC_ALPHA = 3; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_BLEND_COLOR_READ, + ALLOW_COLOR_TABLE_READ, + ALLOW_COMBINE_READ, + ALLOW_MODE_READ, + ALLOW_TRANSFORM_READ + }; + + /** + * Constructs a TextureAttributes object with default parameters. + * The default values are as follows: + *
    + * texture mode : REPLACE
    + * blend color : black (0,0,0,0)
    + * transform : identity
    + * perspective correction mode : NICEST
    + * texture color table : null
    + * combine rgb mode : COMBINE_MODULATE
    + * combine alpha mode : COMBINE_MODULATE
    + * combine rgb source : + *
      + * C0=COMBINE_TEXTURE_COLOR
      + * C1=COMBINE_PREVIOUS_TEXTURE_UNIT_STATE
      + * C2=COMBINE_CONSTANT_COLOR
      + *
    + * combine alpha source : + *
      + * C0=COMBINE_TEXTURE_COLOR
      + * C1=COMBINE_PREVIOUS_TEXTURE_UNIT_STATE
      + * C2=COMBINE_CONSTANT_COLOR
      + *
    + * combine rgb function : COMBINE_SRC_COLOR
    + * combine alpha function : COMBINE_SRC_ALPHA
    + * combine rgb scale : 1
    + * combine alpha scale : 1
    + *
+ */ + public TextureAttributes() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Constructs a TextureAttributes object with the specified values. + * @param textureMode the texture mode; one of MODULATE, + * DECAL, BLEND, REPLACE, or + * COMBINE + * @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 NICEST or FASTEST + * @exception IllegalArgumentException if textureMode + * is a value other than MODULATE, + * DECAL, BLEND, REPLACE, or + * COMBINE + * @exception IllegalArgumentException if mode value is other + * than FASTEST or NICEST. + */ + 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")); + } + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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: MODULATE, + * DECAL, BLEND, REPLACE, or + * COMBINE + * @exception IllegalArgumentException if textureMode + * is a value other than MODULATE, + * DECAL, BLEND, REPLACE, or + * COMBINE + * + * @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 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. + * @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 FASTEST or NICEST. + */ + 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 (r, g, b, + * and a, respectively) and then by color value; + * table.length defines the number of color + * components and table[0].length defines the texture + * color table size. If the table is non-null, the number of + * color components must either be 3, for rgb data, or 4, + * for rgba 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. + * + *

+ * When enabled, the texture color table is applied after the + * texture filtering operation and before texture application. + * Each of the r, g, b, and a + * components are clamped to the range [0,1], multiplied by + * textureColorTableSize-1, 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 table.length + * 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, + * int[numTextureColorTableComponents][textureColorTableSize]). + * + * @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: + * COMBINE_REPLACE, + * COMBINE_MODULATE, COMBINE_ADD, + * COMBINE_ADD_SIGNED, COMBINE_SUBTRACT, + * COMBINE_INTERPOLATE, or COMBINE_DOT3 + * + * @exception IllegalArgumentException if combineMode + * is a value other than COMBINE_REPLACE, + * COMBINE_MODULATE, COMBINE_ADD, + * COMBINE_ADD_SIGNED, COMBINE_SUBTRACT, + * COMBINE_INTERPOLATE, or COMBINE_DOT3 + * @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: + * COMBINE_REPLACE, + * COMBINE_MODULATE, COMBINE_ADD, + * COMBINE_ADD_SIGNED, COMBINE_SUBTRACT, + * COMBINE_INTERPOLATE, or COMBINE_DOT3 + * + * @exception IllegalArgumentException if combineMode + * is a value other than COMBINE_REPLACE, + * COMBINE_MODULATE, COMBINE_ADD, + * COMBINE_ADD_SIGNED, COMBINE_SUBTRACT, + * COMBINE_INTERPOLATE, or COMBINE_DOT3 + * @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: COMBINE_OBJECT_COLOR, + * COMBINE_TEXTURE_COLOR, + * COMBINE_CONSTANT_COLOR, or + * COMBINE_PREVIOUS_TEXTURE_UNIT_STATE + * + * @exception IndexOutOfBoundsException if index < 0 or + * index > 2 + * @exception IllegalArgumentException if src + * is a value other than COMBINE_OBJECT_COLOR, + * COMBINE_TEXTURE_COLOR, + * COMBINE_CONSTANT_COLOR, or + * COMBINE_PREVIOUS_TEXTURE_UNIT_STATE + * @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: COMBINE_OBJECT_COLOR, + * COMBINE_TEXTURE_COLOR, + * COMBINE_CONSTANT_COLOR, or + * COMBINE_PREVIOUS_TEXTURE_UNIT_STATE + * + * @exception IndexOutOfBoundsException if index < 0 or + * index > 2 + * @exception IllegalArgumentException if src + * is a value other than COMBINE_OBJECT_COLOR, + * COMBINE_TEXTURE_COLOR, + * COMBINE_CONSTANT_COLOR, or + * COMBINE_PREVIOUS_TEXTURE_UNIT_STATE + * @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 index < 0 or + * index > 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 index < 0 or + * index > 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: + * COMBINE_SRC_COLOR, + * COMBINE_ONE_MINUS_SRC_COLOR, + * COMBINE_SRC_ALPHA, or + * COMBINE_ONE_MINUS_SRC_ALPHA + * + * @exception IndexOutOfBoundsException if index < 0 or + * index > 2 + * @exception IllegalArgumentException if function + * is a value other than COMBINE_SRC_COLOR, + * COMBINE_ONE_MINUS_SRC_COLOR, + * COMBINE_SRC_ALPHA, or + * COMBINE_ONE_MINUS_SRC_ALPHA + * @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: + * COMBINE_SRC_ALPHA, or + * COMBINE_ONE_MINUS_SRC_ALPHA + * + * @exception IndexOutOfBoundsException if index < 0 or + * index > 2 + * @exception IllegalArgumentException if function + * is a value other than + * COMBINE_SRC_ALPHA or + * COMBINE_ONE_MINUS_SRC_ALPHA + * @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 index < 0 or + * index > 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 index < 0 or + * index > 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 scale 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 scale 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. + */ + @Override + void createRetained() { + this.retained = new TextureAttributesRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + TextureAttributes ta = new TextureAttributes(); + ta.duplicateNodeComponent(this); + return ta; + } + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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); + } + // start fix issue 636 + rt.initCombineRgbMode(attr.getCombineRgbMode()); + rt.initCombineAlphaMode(attr.getCombineAlphaMode()); + + rt.initCombineRgbScale(attr.getCombineRgbScale()); + rt.initCombineAlphaScale(attr.getCombineAlphaScale()); + + // Check one of the combine source or function arrays + if (attr.combineRgbSrc != null) { + for (int i=0; i < 3; i++) { + rt.initCombineRgbSource(i, attr.getCombineRgbSource(i)); + rt.initCombineAlphaSource(i, attr.getCombineAlphaSource(i)); + rt.initCombineRgbFunction(i, attr.getCombineRgbFunction(i)); + rt.initCombineAlphaFunction(i, attr.getCombineAlphaFunction(i)); + } + } + // end fix + + } +} diff --git a/src/javax/media/j3d/TextureAttributesRetained.java b/src/javax/media/j3d/TextureAttributesRetained.java new file mode 100644 index 0000000..a31d8fc --- /dev/null +++ b/src/javax/media/j3d/TextureAttributesRetained.java @@ -0,0 +1,996 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Color4f; + +/** + * 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 enums. Currently only supports 0 - 9. + static final Integer[] enums; + + // 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 { + // 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 + enums = new Integer[10]; + for (int i = 0; i < enums.length; i++) { + enums[i] = new Integer(i); + } + } + + TextureAttributesRetained() {} + + // 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, new Transform3D(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; + } + + void updateNative(Canvas3D cv, boolean simulate, int textureFormat) { + + //System.err.println("TextureAttributes/updateNative: simulate= " + simulate + " " + this); + + //if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_COLOR_TABLE) + // == 0) && textureColorTable != null) { + // System.err.println("TextureColorTable Not supported"); + //} + + //System.err.println("textureMode= " + textureMode); + boolean isIdentity = + ((transform.getType() & Transform3D.IDENTITY) != 0); + + if (simulate == false) { + if (VirtualUniverse.mc.useCombiners && + (cv.textureExtendedFeatures & + Canvas3D.TEXTURE_REGISTER_COMBINERS) != 0) { + Pipeline.getPipeline().updateRegisterCombiners(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; + + Pipeline.getPipeline().updateTextureAttributes(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; + } + + Pipeline.getPipeline().updateCombiner(cv.ctx, + _combineRgbMode, _combineAlphaMode, + combineRgbSrc, combineAlphaSrc, + combineRgbFcn, combineAlphaFcn, + combineRgbScale, combineAlphaScale); + + } else { + + // Texture COMBINE is not supported by the underlying + // layer, fallback to REPLACE + + Pipeline.getPipeline().updateTextureAttributes(cv.ctx, + transform.mat, isIdentity, + TextureAttributes.REPLACE, + perspCorrectionMode, + textureBlendColor.x, textureBlendColor.y, + textureBlendColor.z, textureBlendColor.w, + textureFormat); + } + } else { + Pipeline.getPipeline().updateTextureAttributes(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) { + + Pipeline.getPipeline().updateTextureColorTable(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 + Pipeline.getPipeline().updateTextureAttributes(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) { + + Pipeline.getPipeline().updateTextureColorTable(cv.ctx, numTextureColorTableComponents, + textureColorTableSize, textureColorTable); + } + + switch (textureMode) { + case TextureAttributes.COMBINE: + case TextureAttributes.REPLACE: + cv.setBlendFunc(cv.ctx, + TransparencyAttributes.BLEND_ONE, + TransparencyAttributes.BLEND_ZERO); + break; + case TextureAttributes.MODULATE: + cv.setBlendFunc(cv.ctx, + TransparencyAttributes.BLEND_DST_COLOR, + TransparencyAttributes.BLEND_ZERO); + break; + case TextureAttributes.DECAL: + if (textureFormat == Texture.RGBA) { + cv.setBlendFunc(cv.ctx, + TransparencyAttributes.BLEND_SRC_ALPHA, + TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA); + } else { + cv.setBlendFunc(cv.ctx, + TransparencyAttributes.BLEND_ONE, + TransparencyAttributes.BLEND_ZERO); + } + break; + case TextureAttributes.BLEND: + cv.setBlendColor(cv.ctx, textureBlendColor.x, textureBlendColor.y, + textureBlendColor.z, textureBlendColor.w); + cv.setBlendFunc(cv.ctx, + TransparencyAttributes.BLEND_CONSTANT_COLOR, + TransparencyAttributes.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 + */ + @Override + 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 + */ + @Override + 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); + } + 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; + } + + } + + + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + } + + @Override + 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/javax/media/j3d/TextureBin.java b/src/javax/media/j3d/TextureBin.java new file mode 100644 index 0000000..9af0cc1 --- /dev/null +++ b/src/javax/media/j3d/TextureBin.java @@ -0,0 +1,1543 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; + +/** + * 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; + + // number of active texture unit + private int numActiveTexUnit; + + /** + * The RenderBin for this object + */ + RenderBin renderBin = null; + + /** + * The EnvironmentSet that this TextureBin resides + */ + EnvironmentSet environmentSet = null; + + /** + * The AttributeBin that this TextureBin resides + */ + AttributeBin attributeBin = null; + + /** + * The ShaderBin that this TextureBin resides + */ + ShaderBin shaderBin = 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; + + // Issue 249 - check for sole user only if property is set + // determine if this appearance is a sole user of this + // TextureBin + tbFlag &= ~TextureBin.SOLE_USER; + if (VirtualUniverse.mc.allowSoleUser) { + if ((app != null) && + (app.changedFrequent & + (AppearanceRetained.TEXTURE | + AppearanceRetained.TEXCOORD_GEN | + AppearanceRetained.TEXTURE_ATTR | + AppearanceRetained.TEXTURE_UNIT_STATE)) != 0) { + tbFlag |= TextureBin.SOLE_USER; + + } + } + + if ((tbFlag & TextureBin.SOLE_USER) != 0) { + this.app = app; + } else { + 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; + boolean foundDisableUnit = false; + numActiveTexUnit = 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.getTextureBinRefCount(this) == 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 units. Note that this total number + // now includes disabled units so that there is always + // a one-to-one mapping. We no longer remap texture units. + if (texUnitState[i].isTextureEnabled()) { + numActiveTexUnit = i + 1; + + 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.getTextureBinRefCount(this) == 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) { + // 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.err.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 (int 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.err.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; + 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 units. Note that this total number + // now includes disabled units so that there is always + // a one-to-one mapping. We no longer remap texture units. + if (texUnitState[i] != null && + texUnitState[i].isTextureEnabled()) { + numActiveTexUnit = i + 1; + + 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; + } + + @Override + 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 || + environmentSet.lightBin.geometryBackground != null)) { + // System.err.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; + Collection> c = addRMs.values(); + Iterator> listIterator = c.iterator(); + RenderMolecule renderMoleculeList, head; + + while (listIterator.hasNext()) { + boolean changed = false; + ArrayList curList = listIterator.next(); + RenderMolecule r = 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 = 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 = 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; + } + + + // XXXX: 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) { + r.textureBin = this; + + HashMap> map; + if (r.isOpaqueOrInOG) + map = addOpaqueRMs; + else + map = addTransparentRMs; + + ArrayList list = map.get(r.localToVworld); + if (list == 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) { + int index; + boolean found = false; + RenderMolecule 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 + ArrayList list = addMap.get(r.localToVworld); + if (list != 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; + 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 || + 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); + } + + shaderBin.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) { + + 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; + + // Get the number of available texture units; this depends on + // whether or not shaders are being used. + boolean useShaders = (shaderBin.shaderProgram != null); + int availableTextureUnits = + useShaders ? cv.maxTextureImageUnits : cv.maxTextureUnits; + + // If the number of active texture units is greater than the number of + // supported units, then we + // need to set a flag indicating that the texture units are invalid. + boolean disableTexture = false; + + if (numActiveTexUnit > availableTextureUnits) { + disableTexture = true; +// System.err.println("*** TextureBin : number of texture units exceeded"); + } + + // set the number active texture unit in Canvas3D + if (disableTexture) { + cv.setNumActiveTexUnit(0); + } + else { + cv.setNumActiveTexUnit(numActiveTexUnit); + } + + // state update + if (numActiveTexUnit <= 0 || disableTexture) { + 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 { + + int j = 0; + + 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; + } else { + if (j <= cv.getLastActiveTexUnit()) { + cv.resetTexture(cv.ctx, j); + } + } + + 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); + + // set the active texture unit back to 0 + cv.activeTextureUnit(cv.ctx, 0); + + } + cv.canvasDirty &= ~Canvas3D.TEXTUREBIN_DIRTY; + } + + +/** + * Renders this TextureBin + */ +void render(Canvas3D cv) { + render(cv, opaqueRMList); +} + +void render(Canvas3D cv, RenderMolecule rlist) { + // include this TextureBin to the to-be-updated state set in canvas + cv.setStateToUpdate(Canvas3D.TEXTUREBIN_BIT, this); + renderList(cv, USE_DISPLAYLIST, rlist); +} + +void render(Canvas3D cv, TransparentRenderingInfo rlist) { + // include this TextureBin to the to-be-updated state set in canvas + cv.setStateToUpdate(Canvas3D.TEXTUREBIN_BIT, this); + renderList(cv, USE_DISPLAYLIST, rlist); +} + + /** + * render list of RenderMolecule + */ + void renderList(Canvas3D cv, int pass, RenderMolecule rlist) { + assert pass < 0; + + // 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) { + assert pass < 0; + + RenderMolecule rm = tinfo.rm; + if (rm.isSwitchOn()) { + rm.transparentSortRender(cv, pass, tinfo); + } + } + + + void changeLists(RenderMolecule r) { + RenderMolecule renderMoleculeList, rmlist = null, head; + HashMap allMap = null; + boolean newRM = false; + // System.err.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 || + environmentSet.lightBin.geometryBackground != null)) { + renderBin.removeTransparentObject(this); + } + // Issue 129: remove the RM's render atoms from the + // list of transparent render atoms + if ((renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) && + (environmentSet.lightBin.geometryBackground == null)) { + r.addRemoveTransparentObject(renderBin, false); + } + } + } + HashMap renderMoleculeMap; + RenderMolecule startList; + + // Now insert in the other bin + r.evalAlphaUsage(shaderBin.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 = 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; + } + else { + r.nextMap = startList; + startList.prevMap = r; + startList = r; + startList.nextMap.checkEquivalenceWithLeftNeighbor(r,RenderMolecule.ALL_DIRTY_BITS); + } + // Issue 67 : since we are adding the new RM at the head, we must + // set all dirty bits unconditionally + startList.dirtyAttrsAcrossRms = 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 || + environmentSet.lightBin.geometryBackground != null)) { + transparentRMList = startList; + renderBin.addTransparentObject(this); + } + else { + transparentRMList = startList; + } + // Issue 129: add the RM's render atoms to the list of + // transparent render atoms + // XXXX: do we need to resort the list after the add??? + if ((renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) && + (environmentSet.lightBin.geometryBackground == null)) { + r.addRemoveTransparentObject(renderBin, true); + } + } + } + + 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 shaderBin that this textureBin goes to + // zombie state + + shaderBin.decrActiveTextureBin(); + } + } + + void incrActiveRenderMolecule() { + + if (numEditingRenderMolecules == 0) { + + // if this textureBin is in zombie state, inform + // the shaderBin that this textureBin is activated again. + + shaderBin.incrActiveTextureBin(); + } + + numEditingRenderMolecules++; + } +} diff --git a/src/javax/media/j3d/TextureCubeMap.java b/src/javax/media/j3d/TextureCubeMap.java new file mode 100644 index 0000000..10fbdd8 --- /dev/null +++ b/src/javax/media/j3d/TextureCubeMap.java @@ -0,0 +1,403 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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: + * + *

    + *
  • POSITIVE_X
  • + *
  • NEGATIVE_X
  • + *
  • POSITIVE_Y
  • + *
  • NEGATIVE_Y
  • + *
  • POSITIVE_Z
  • + *
  • NEGATIVE_Z
  • + *
+ * + *

+ * Note that as of Java 3D 1.5, the texture width and height are no longer + * required to be an exact power of two. However, not all graphics devices + * supports non-power-of-two textures. If non-power-of-two texture mapping is + * unsupported on a particular Canvas3D, textures with a width or height that + * are not an exact power of two are ignored for that canvas. + * + * @see Canvas3D#queryProperties + * + * @since Java 3D 1.3 + */ +public class TextureCubeMap extends Texture { + + // TODO KCR : NPOT + + /** + * 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. + * Note also that a texture with a non-power-of-two width will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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 (and height) of image at level 0. + * @exception IllegalArgumentException if width is not greater + * than 0 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. + * Note also that a texture with a non-power-of-two width will + * only be rendered on a graphics device that supports non-power-of-two + * textures. + * + * @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 (and height) of image at level 0. This + * does not include the width of the boundary. + * @param boundaryWidth width of the boundary, which must be 0 or 1. + * + * @exception IllegalArgumentException if width is not + * greater than 0 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: + * POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * @param image ImageComponent2D object containing the image + * + * @exception IllegalArgumentException if + * face has a value other + * than POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalSharingException if this TextureCubeMap is live and + * the specified image is being used by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this TextureCubeMap is + * being used by an immediate mode context and + * the specified image is being used by a Canvas3D as an off-screen buffer. + */ + public void setImage(int level, int face, ImageComponent2D image) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_IMAGE_WRITE)) + throw new CapabilityNotSetException( + J3dI18N.getString("TextureCubeMap1")); + } + + validateImageIllegalSharing(image); + + 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: + * POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * @param images array of ImageComponent2D objects containing the images + * + * @exception IllegalArgumentException if + * face has a value other + * than POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * + * @exception IllegalSharingException if this TextureCubeMap is live and + * any of the specified images are being used by a Canvas3D as an + * off-screen buffer. + * + * @exception IllegalSharingException if this TextureCubeMap is + * being used by an immediate mode context and + * any of the specified images are being used by a Canvas3D as an + * off-screen buffer. + */ + public void setImages(int face, ImageComponent2D[] images) { + if (isLiveOrCompiled()) { + if(!this.getCapability(ALLOW_IMAGE_WRITE)) + throw new CapabilityNotSetException( + J3dI18N.getString("TextureCubeMap1")); + } + + // Do illegal sharing check + for(int i=0; iPOSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * @return the ImageComponent object containing the texture image at + * the specified mipmap level. + * + * @exception IllegalArgumentException if + * face has a value other + * than POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * + * @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: + * POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * @return an array of ImageComponent object for the particular face of + * of the cube map. + * + * @exception IllegalArgumentException if + * face has a value other + * than POSITIVE_X, NEGATIVE_X, + * POSITIVE_Y, NEGATIVE_Y, + * POSITIVE_Z or NEGATIVE_Z. + * + * @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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + 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 + */ + @Override + public ImageComponent[] getImages() { + throw new UnsupportedOperationException(); + } + + + /** + * Creates a retained mode TextureCubeMapRetained object that this + * TextureCubeMap component object will point to. + */ + @Override + 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) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } +} + diff --git a/src/javax/media/j3d/TextureCubeMapRetained.java b/src/javax/media/j3d/TextureCubeMapRetained.java new file mode 100644 index 0000000..97fa9ff --- /dev/null +++ b/src/javax/media/j3d/TextureCubeMapRetained.java @@ -0,0 +1,355 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * TextureCubeMap is a subclass of Texture class. + */ +class TextureCubeMapRetained extends TextureRetained { + + + static final int NUMFACES = 6; + + + @Override + 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) { + + // Issue 172 : call checkImageSize even for non-live setImage calls + checkImageSize(level, 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); + } + } + + /* Don't think this is needed. --- Chien. + ((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) { + + 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.getRefImage(0) == null) { + enable = false; + } + } + else { + if (img.getImageData(isUseAsRaster()).get() == 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; + + 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.getRefImage(0) == null) { + enable = false; + } + } + else { + if (img.getImageData(isUseAsRaster()).get() == 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; + } + + + @Override + void bindTexture(Context ctx, int objectId, boolean enable) { + Pipeline.getPipeline().bindTextureCubeMap(ctx, objectId, enable); + } + + @Override + void updateTextureBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + + Pipeline.getPipeline().updateTextureCubeMapBoundary(ctx, + boundaryModeS, boundaryModeT, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + @Override + void updateTextureFilterModes(Context ctx, + int minFilter, int magFilter) { + + Pipeline.getPipeline().updateTextureCubeMapFilterModes(ctx, + minFilter, magFilter); + } + + @Override + void updateTextureSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + + Pipeline.getPipeline().updateTextureCubeMapSharpenFunc(ctx, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + @Override + void updateTextureFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + + Pipeline.getPipeline().updateTextureCubeMapFilter4Func(ctx, + numFilter4FuncPts, filter4FuncPts); + } + + @Override + void updateTextureAnisotropicFilter(Context ctx, float degree) { + Pipeline.getPipeline().updateTextureCubeMapAnisotropicFilter(ctx, degree); + } + + + @Override + void updateTextureLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + + Pipeline.getPipeline().updateTextureCubeMapLodRange(ctx, baseLevel, maximumLevel, + minimumLod, maximumLod); + } + + @Override + void updateTextureLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + + Pipeline.getPipeline().updateTextureCubeMapLodOffset(ctx, + lodOffsetX, lodOffsetY, lodOffsetZ); + } + + + /** + * Load level 0 explicitly with null data pointer to allow + * mipmapping when level 0 is not the base level + */ + @Override + void updateTextureDimensions(Canvas3D cv) { + if(images[0][0] != null) { + // All faces should have the same image format and type. + int imageFormat = images[0][0].getImageFormatTypeIntValue(false); + int imageType = images[0][0].getImageDataTypeIntValue(); + + for (int i = 0; i < 6; i++) { + updateTextureImage(cv, i, maxLevels, 0, + format, imageFormat, + width, height, boundaryWidth, + imageType, null); + } + } + } + + // This is just a wrapper of the native method. + @Override + void updateTextureImage(Canvas3D cv, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, int imageDataType, + Object imageData) { + + Pipeline.getPipeline().updateTextureCubeMapImage(cv.ctx, + face, numLevels, level, + textureFormat, imageFormat, + width, height, + boundaryWidth, imageDataType, imageData, useAutoMipMapGeneration(cv)); + } + + // This is just a wrapper of the native method. + @Override + void updateTextureSubImage(Canvas3D cv, + int face, int level, + int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object imageData) { + + Pipeline.getPipeline().updateTextureCubeMapSubImage(cv.ctx, + face, level, xoffset, yoffset, + textureFormat, imageFormat, + imgXOffset, imgYOffset, + tilew, width, height, + imageDataType, imageData, useAutoMipMapGeneration(cv)); + + } +} diff --git a/src/javax/media/j3d/TextureRetained.java b/src/javax/media/j3d/TextureRetained.java new file mode 100644 index 0000000..fdb16ee --- /dev/null +++ b/src/javax/media/j3d/TextureRetained.java @@ -0,0 +1,2515 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.image.DataBufferByte; +import java.awt.image.RenderedImage; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.vecmath.Color4f; +import javax.vecmath.Point2f; +import javax.vecmath.Point3f; +import javax.vecmath.Tuple3f; + +/** + * 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) + + // true if width or height is non power of two + private boolean widthOrHeightIsNPOT = false; + // Array of images (one for each mipmap level) + ImageComponentRetained images[][]; + // maximum number of levels needed for the mipmapMode of this texture + int maxLevels = 0; + // maximum number of mipmap levels that can be defined for this texture + private int maxMipMapLevels = 0; + + 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; + + private boolean useAsRaster = false; // true if used by Raster or Background. + + // 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[]; + + // Issue 357 - we need a separate reference counter per RenderBin, since + // each RenderBin keeps an independent list of Texture objects to be freed. + // Since this is accessed infrequently, we will use a HashMap for the + // textureBin reference counter + private HashMap textureBinRefCount = + new HashMap(); + + // need to synchronize access from multiple rendering threads + Object resourceLock = new Object(); + + private static boolean isPowerOfTwo(int val) { + return ((val & (val - 1)) == 0); + } + + void initialize(int format, int width, int widLevels, + int height, int heiLevels, int mipmapMode, + int boundaryWidth) { + + this.mipmapMode = mipmapMode; + this.format = format; + this.width = width; + this.height = height; + this.boundaryWidth = boundaryWidth; + + if(!isPowerOfTwo(width) || !isPowerOfTwo(height)) { + this.widthOrHeightIsNPOT = true; + } + + // determine the maximum number of mipmap levels that can be + // defined from the specified dimension + + if (widLevels > heiLevels) { + maxMipMapLevels = widLevels + 1; + } else { + maxMipMapLevels = heiLevels + 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; + } + } + } + + 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) { + + // Issue 172 : call checkImageSize even for non-live setImage calls + checkImageSize(level, 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 (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 imgWidth = ((ImageComponentRetained)image.retained).width; + int imgHeight = ((ImageComponentRetained)image.retained).height; + + int wdh = width; + int hgt = height; + for (int i = 0; i < level; i++) { + wdh >>= 1; + hgt >>= 1; + } + + if (wdh < 1) wdh = 1; + if (hgt < 1) hgt = 1; + + if ((wdh != (imgWidth - 2*boundaryWidth)) || + (hgt != (imgHeight - 2*boundaryWidth))) { + throw new IllegalArgumentException( + J3dI18N.getString("TextureRetained1")); + } + } + } + + final void checkSizes(ImageComponentRetained images[]) { + // Issue 172 : this method is now redundant + + // Assertion check that the image at each level is the correct size + // This shouldn't be needed since we already should have checked the + // size at each level, and verified that all levels are set. + if (images != null) { + int hgt = height; + int wdh = width; + for (int level = 0; level < images.length; level++) { + int imgWidth = images[level].width; + int imgHeight = images[level].height; + + assert (wdh == (imgWidth - 2*boundaryWidth)) && + (hgt == (imgHeight - 2*boundaryWidth)); + + wdh /= 2; + hgt /= 2; + if (wdh < 1) wdh = 1; + if (hgt < 1) hgt = 1; + } + } + } + + final void setImage(int level, ImageComponent 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.getRefImage(0) == null) { + enable = false; + } + } + else { + if (img.getImageData(isUseAsRaster()).get() == 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; + + 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.getRefImage(0) == null) { + enable = false; + } + } + else { + if (img.getImageData(isUseAsRaster()).get() == 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].getRefImage(0) == null) { + enable = false; + } + } else { + if (images[j][i].getImageData(isUseAsRaster()).get() == 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")); + } + if((mipmapMode == Texture.BASE_LEVEL) && (level != 0)) { + throw new IllegalArgumentException( + J3dI18N.getString("Texture48")); + } + + 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; + } + + + + + @Override + void setLive(boolean backgroundGroup, int refCount) { + + // This line should be assigned before calling doSetLive, so that + // the mirror object's enable is assigned correctly! + enable = userSpecifiedEnable; + + super.doSetLive(backgroundGroup, refCount); + + // XXXX: 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); + } + } + } + + // Issue 172 : assertion check the sizes of the images after we have + // checked for all mipmap levels being set + if (images != null) { + for (int j = 0; j < numFaces; j++) { + checkSizes(images[j]); + } + } + + // Send a message to Rendering Attr stucture to update the resourceMask + J3dMessage createMessage = new J3dMessage(); + 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].getRefImage(0) == null) { + enable = false; + } + } else { + if (images[j][i].getImageData(isUseAsRaster()).get() == null) { + enable = false; + } + } + } + } + } else { + enable = false; + } + if (!enable) + sendMessage(ENABLE_CHANGED, Boolean.FALSE); + } + + super.markAsLive(); + } + + @Override + 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); + } + } + } + } + + /* + * The following methods update the native context. + * The implementation for Texture2D happens here. + * Texture3D and TextureCubeMap implement their own versions. + */ + + void bindTexture(Context ctx, int objectId, boolean enable) { + Pipeline.getPipeline().bindTexture2D(ctx, objectId, enable); + } + + void updateTextureBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + + Pipeline.getPipeline().updateTexture2DBoundary(ctx, + boundaryModeS, boundaryModeT, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + void updateTextureFilterModes(Context ctx, + int minFilter, int magFilter) { + + Pipeline.getPipeline().updateTexture2DFilterModes(ctx, + minFilter, magFilter); + } + + void updateTextureSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + + Pipeline.getPipeline().updateTexture2DSharpenFunc(ctx, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + void updateTextureFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + + Pipeline.getPipeline().updateTexture2DFilter4Func(ctx, + numFilter4FuncPts, filter4FuncPts); + } + + void updateTextureAnisotropicFilter(Context ctx, float degree) { + Pipeline.getPipeline().updateTexture2DAnisotropicFilter(ctx, degree); + } + + void updateTextureLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + + Pipeline.getPipeline().updateTexture2DLodRange(ctx, baseLevel, maximumLevel, + minimumLod, maximumLod); + } + + void updateTextureLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + + Pipeline.getPipeline().updateTexture2DLodOffset(ctx, + lodOffsetX, lodOffsetY, lodOffsetZ); + } + + // free a Texture2D id + void freeTextureId(int id) { + synchronized (resourceLock) { + if (objectId == id) + objectId = -1; + } + } + + private boolean isEnabled(Canvas3D cv) { + if(widthOrHeightIsNPOT && !isUseAsRaster() && + ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_NON_POWER_OF_TWO ) == 0)) { + return false; + } + return enable; + } + + + // bind a named texture to a texturing target + void bindTexture(Canvas3D cv) { + + synchronized(resourceLock) { + if (objectId == -1) + objectId = Canvas3D.generateTexID(cv.ctx); + cv.addTextureResource(objectId, this); + } + bindTexture(cv.ctx, objectId, isEnabled(cv)); + } + + + /** + * load level 0 explicitly with null pointer to enable + * mipmapping when level 0 is not the base level + */ + void updateTextureDimensions(Canvas3D cv) { + if(images[0][0] != null) { + updateTextureImage(cv, 0, maxLevels, 0, + format, images[0][0].getImageFormatTypeIntValue(false), + width, height, boundaryWidth, + images[0][0].getImageDataTypeIntValue(), null); + } + } + + + void updateTextureLOD(Canvas3D cv) { + + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_RANGE) != 0 ) { + + int max = 0; + if( mipmapMode == Texture.BASE_LEVEL ) { + max = maxMipMapLevels; + } + else { + max = maximumLevel; + } + + updateTextureLodRange(cv.ctx, baseLevel, max, + 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 ((magnificationFilter >= Texture.LINEAR_SHARPEN) && + (magnificationFilter <= 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 ((magnificationFilter >= Texture2D.LINEAR_DETAIL) && + (magnificationFilter <= 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 (minificationFilter == Texture.FILTER4 || magnificationFilter == 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 (minificationFilter == Texture.FILTER4) { + minificationFilter = Texture.BASE_LEVEL_LINEAR; + } + if (magnificationFilter == Texture.FILTER4) { + magnificationFilter = Texture.BASE_LEVEL_LINEAR; + } + } + } + + // Fallback to BASE mode if hardware mipmap generation is not supported. + if ((mipmapMode == Texture.BASE_LEVEL) && ((cv.textureExtendedFeatures & + Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION) == 0)) { + + if (minificationFilter == Texture.NICEST || + minificationFilter == Texture.MULTI_LEVEL_LINEAR) { + minificationFilter = Texture.BASE_LEVEL_LINEAR; + } else if (minificationFilter == Texture.MULTI_LEVEL_POINT) { + minificationFilter = Texture.BASE_LEVEL_POINT; + } + } + + // 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 around the native call for 2D textures; overridden for + // TextureCureMap + void updateTextureImage(Canvas3D cv, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int imageDataType, Object data) { + + Pipeline.getPipeline().updateTexture2DImage(cv.ctx, + numLevels, level, + textureFormat, imageFormat, + width, height, boundaryWidth, + imageDataType, data, useAutoMipMapGeneration(cv)); + } + + // Wrapper around the native call for 2D textures; overridden for + // TextureCureMap + void updateTextureSubImage(Canvas3D cv, + int face, int level, + int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object data) { + + Pipeline.getPipeline().updateTexture2DSubImage(cv.ctx, + level, xoffset, yoffset, + textureFormat, imageFormat, + imgXOffset, imgYOffset, + tilew, width, height, + imageDataType, data, useAutoMipMapGeneration(cv)); + } + + + /** + * 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) { + + boolean useAsRaster = isUseAsRaster(); + ImageComponentRetained.ImageData imageData = image.getImageData(useAsRaster); + + assert imageData != null; + + updateTextureImage(cv, + face, numLevels, level, + format, image.getImageFormatTypeIntValue(useAsRaster), + imageData.getWidth(), imageData.getHeight(), + boundaryWidth, image.getImageDataTypeIntValue(), + imageData.get()); + + + // TODO : Dead code - need to clean up for 1.6 + // Now take care of the RenderedImage (byRef and yUp) case. Note, if image + // is a RenderedImage ( byRef and yUp), then imageData will be null + + if (imageData == null) { + // System.err.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.tilew; + int endYTile = image.tileh; + int curw = endXTile; + int curh = endYTile; + + 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 = 0; m < image.numYTiles; m++) { + xoffset = 0; + tmpw = width; + curw = startw; + imageXOffset = image.tilew - curw; + for (int n = 0; n < image.numXTiles; n++) { + java.awt.image.Raster ras; + ras = ((RenderedImage)image.getRefImage(0)).getTile(n,m); + byte[] data = ((DataBufferByte)ras.getDataBuffer()).getData(); + updateTextureSubImage(cv, face, + level, xoffset, yoffset, format, + image.getImageFormatTypeIntValue(false), + imageXOffset, imageYOffset, + image.tilew, + curw, curh, + ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY, + (Object) data); + 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.err.println("\nupdateTextureSubImage: x= " + x + " y= " + y + + // " width= " + width + " height= " + height + + // " format= " + format); + + ImageComponentRetained.ImageData imageData = image.getImageData(isUseAsRaster()); + if(imageData != null) { + int xoffset = x; + int yoffset = y; + + // TODO Check this logic : If !yUp adjust yoffset + if (!image.yUp) { + yoffset = image.height - yoffset - height; + } + + updateTextureSubImage(cv, face, level, + xoffset, yoffset, + format, image.getImageFormatTypeIntValue(false), + xoffset, yoffset, + image.width, width, height, + image.getImageDataTypeIntValue(), + imageData.get()); + + } else { + + assert false; + + // TODO : Dead code - need to clean up for 1.6 + // System.err.println("RenderedImage subImage update"); + // determine the first tile of the image + + float mt; + int minTileX, minTileY; + + int rx = x; + int ry = y; + + mt = (float)(rx) / (float)image.tilew; + if (mt < 0) { + minTileX = (int)(mt - 1); + } else { + minTileX = (int)mt; + } + + mt = (float)(ry) / (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; + int startYTile = minTileY * image.tilew; + + + // 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; + + 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 = ((RenderedImage)image.getRefImage(0)).getTile(xTile, yTile); + byte[] data = ((DataBufferByte)ras.getDataBuffer()).getData(); + + updateTextureSubImage(cv, face, level, + textureX, textureY, + format, image.getImageFormatTypeIntValue(false), + imgX, imgY, + image.tilew, curw, curh, + ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY, + (Object)data); + + // 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.err.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 + + ImageComponentRetained image = images[j][i]; + if (image != null) { + // Issue 366: call evaluateExtensions, since it may not + // have been called yet in all cases + image.evaluateExtensions(cv); + reloadTextureImage(cv, j, i, image, maxLevels); + } + } + } + } + + + // update texture mipmap based on the imageUpdateInfo + + void updateTexture(Canvas3D cv, int resourceBit) { + + //System.err.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 (!isEnabled(cv)) { + 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.err.println("Texture/updateNative: " + this + "object= " + objectId + " enable= " + enable); + + bindTexture(cv); + + // if texture is not enabled, don't bother downloading the + // the texture state + + if (!isEnabled(cv)) { + return; + } + + if (cv.useSharedCtx && cv.screen.renderer.sharedCtx != null) { + + 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; + } + } + } + +//System.err.println("......... reloadTexture= " + reloadTexture + +// " updateTexture= " + updateTexture + +// " updateTextureLod= " + updateTextureLod); + +//System.err.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; + } + } + } + } + + @Override + 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 + */ + @Override + 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.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; + + 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); + } + } + } + } + } + + boolean useAutoMipMapGeneration(Canvas3D cv) { + if (mipmapMode == Texture.BASE_LEVEL && + (minFilter == Texture.NICEST || + minFilter == Texture.MULTI_LEVEL_POINT || + minFilter == Texture.MULTI_LEVEL_LINEAR) && + ((cv.textureExtendedFeatures & + Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION) != 0)) { + return true; + } + + return false; + } + + /** + * Go through the image update info list + * and remove those that are already done + * by all the resources + */ + void pruneImageUpdateInfo() { + ImageComponentUpdateInfo info; + + //System.err.println("Texture.pruneImageUpdateInfo"); + + for (int k = 0; k < numFaces; k++) { + for (int i = baseLevel; i <= maximumLevel; i++) { + if ((imageUpdatePruneMask[k] & (1<= 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 + 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" + */ + @Override + 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.err.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) { + 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; + } + + void incTextureBinRefCount(TextureBin tb) { + + ImageComponentRetained image; + + setTextureBinRefCount(tb, getTextureBinRefCount(tb) + 1); + + // 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; + + setTextureBinRefCount(tb, getTextureBinRefCount(tb) - 1); + + // 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + @Override + 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; + } + } + + void setUseAsRaster(boolean useAsRaster) { + this.useAsRaster = useAsRaster; + } + + boolean isUseAsRaster() { + return this.useAsRaster; + } + + // Issue 357 - {get/set}TextureBinRefCount now uses a separate reference + // counter per RenderBin. The absence of the RenderBin key in the hash map + // is used to indicate a value of 0. This makes initialization easier, and + // prevents a small amount of garbage accumulating for inactive RenderBins. + + int getTextureBinRefCount(TextureBin tb) { + Integer i = textureBinRefCount.get(tb.renderBin); + return i == null ? 0 : i.intValue(); + } + + private void setTextureBinRefCount(TextureBin tb, int refCount) { + if (refCount == 0) { + textureBinRefCount.remove(tb.renderBin); + } else { + textureBinRefCount.put(tb.renderBin, new Integer(refCount)); + } + } + +} diff --git a/src/javax/media/j3d/TextureUnitState.java b/src/javax/media/j3d/TextureUnitState.java new file mode 100644 index 0000000..93c3b41 --- /dev/null +++ b/src/javax/media/j3d/TextureUnitState.java @@ -0,0 +1,389 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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: + * + *

+ *

    + *
  • Texture - defines the texture image and filtering + * parameters used when texture mapping is enabled. These attributes + * are defined in a Texture object.
  • + * + *

  • 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.
  • + * + *

  • 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.
  • + *

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_STATE_READ + }; + + /** + * 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 + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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) { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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. + */ + @Override + 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 + * + * @exception IllegalSharingException if this TextureUnitState is live and + * the specified texture refers to an ImageComponent2D that is being used + * by a Canvas3D as an off-screen buffer. + * + * @exception IllegalSharingException if this TextureUnitState is + * being used by an immediate mode context and + * the specified texture refers to an ImageComponent2D that is being used + * by a Canvas3D as an off-screen buffer. + */ + public void set(Texture texture, + TextureAttributes textureAttributes, + TexCoordGeneration texCoordGeneration) { + + if (isLiveOrCompiled()) + if (!this.getCapability(ALLOW_STATE_WRITE)) + throw new CapabilityNotSetException(J3dI18N.getString("TextureUnitState0")); + + // Do illegal sharing check + if(texture != null) { + TextureRetained texRetained = (TextureRetained)texture.retained; + ImageComponent[] images = texRetained.getImages(); + if(images != null) { + for(int i=0; inot call this method directly. + * It should only be called by the cloneNode method. + * + * @deprecated replaced with duplicateNodeComponent( + * NodeComponent originalNodeComponent, boolean forceDuplicate) + */ + @Override + public void duplicateNodeComponent(NodeComponent originalNodeComponent) { + checkDuplicateNodeComponent(originalNodeComponent); + } + + /** + * Copies all TextureUnitState information from + * originalNodeComponent into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNodeComponent the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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. + */ + @Override + 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/javax/media/j3d/TextureUnitStateRetained.java b/src/javax/media/j3d/TextureUnitStateRetained.java new file mode 100644 index 0000000..c3b40c6 --- /dev/null +++ b/src/javax/media/j3d/TextureUnitStateRetained.java @@ -0,0 +1,614 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +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); + } + + void updateNative(int unitIndex, Canvas3D cv, + boolean reload, boolean simulate) { + + //System.err.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.err.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 { + + Pipeline.getPipeline().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); + 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); + 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 + */ + @Override + synchronized void createMirrorObject() { + + if (mirror == null) { + TextureUnitStateRetained mirrorTus = + new TextureUnitStateRetained(); + mirror = mirrorTus; + } + mirror.source = source; + initMirrorObject(); + + } + + @Override + 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" + */ + @Override + 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; + } + + @Override + 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; + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + @Override + 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); + } + + + @Override + 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(); + + } + + + @Override + 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); + } + + @Override + boolean isStatic() { + + return (source.capabilityBitsEmpty() && + ((texture == null) || (texture.isStatic())) && + ((texAttrs == null) || (texAttrs.isStatic())) && + ((texGen == null) || (texGen.isStatic()))); + } + + // Issue 209 - enable this method (was previously commented out) + // Simply pass along to the NodeComponent + @Override + 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))))); + } + + + @Override + void setInImmCtx(boolean flag) { + super.setInImmCtx(flag); + if (texture != null) + texture.setInImmCtx(flag); + if (texAttrs != null) + texAttrs.setInImmCtx(flag); + if (texGen != null) + texGen.setInImmCtx(flag); + } + + @Override + boolean getInImmCtx() { + return (super.getInImmCtx() || + ((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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = 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); + } + + @Override + void handleFrequencyChange(int bit) { + switch (bit) { + case TextureUnitState.ALLOW_STATE_WRITE: { + setFrequencyChangeMask(bit, bit); + } + default: + break; + } + } +} diff --git a/src/javax/media/j3d/TimerThread.java b/src/javax/media/j3d/TimerThread.java new file mode 100644 index 0000000..daabac0 --- /dev/null +++ b/src/javax/media/j3d/TimerThread.java @@ -0,0 +1,167 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + // XXXX: need a way to remove/null this condition when all + // soundschedulers are halted + private WakeupOnElapsedTime soundSchedCond = + new WakeupOnElapsedTime(120000); // every 2 minutes + + private volatile boolean running = true; + private boolean waiting = false; + private boolean ready = 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 + + J3dClock.currentTimeMillis(); + add(inputDeviceSchedCond); + } + + void addSoundSchedCond(long wakeupTime) { + // XXXX: 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); + } + } + + @Override + public void run() { + long waitTime = -1; + long time; + WakeupOnElapsedTime cond; + + while (running) { + runMonitor(WAIT, waitTime); + time = J3dClock.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: + // Issue 308 - wait unless ready flag already set + // Note that we can't loop since we need to be able to timeout + // after "waitTime" msec + if (running && !ready) { + waiting = true; + try { + if (waitTime < 0) { + wait(); + } else { + wait(waitTime); + } + } catch (InterruptedException e) {} + waiting = false; + } + ready = false; + break; + case NOTIFY: + ready = true; + if (waiting) { + notify(); + } + break; + case STOP: + running = false; + notify(); + break; + } + } + +} diff --git a/src/javax/media/j3d/Transform3D.java b/src/javax/media/j3d/Transform3D.java new file mode 100644 index 0000000..ac584d0 --- /dev/null +++ b/src/javax/media/j3d/Transform3D.java @@ -0,0 +1,5870 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.AxisAngle4f; +import javax.vecmath.GMatrix; +import javax.vecmath.Matrix3d; +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4d; +import javax.vecmath.Matrix4f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Point4d; +import javax.vecmath.Quat4d; +import javax.vecmath.Quat4f; +import javax.vecmath.SingularMatrixException; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector3f; +import javax.vecmath.Vector4d; +import javax.vecmath.Vector4f; + +/** + * 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.

+ * + * 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.

+ * + * 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:

+ *

    + *
  • ZERO - zero matrix. All of the elements in the matrix + * have the value 0.
  • + *

  • IDENTITY - identity matrix. A matrix with ones on its + * main diagonal and zeros every where else.
  • + *

  • SCALE - the matrix is a uniform scale matrix - there are + * no rotational or translation components.
  • + *

  • 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.
  • + *

  • RIGID - the upper 3 X 3 of the matrix is orthogonal, and + * there is a translation component-the scale is unity.
  • + *

  • 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.
  • + *

  • 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.
  • + *

+ * A matrix is also classified by the sign of its determinant:

+ *

    + * 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.

+ * The Java 3D model for 4 X 4 transformations is:

+ *

    + * [ 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
    + * 

+ * 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 static final int ALL_DIRTY = CLASSIFY_ALL_DIRTY | ROTSCALESVD_DIRTY; + + 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.err.print(" ZERO"); + if ((type & IDENTITY) > 0 ) System.err.print(" IDENTITY"); + if ((type & SCALE) > 0 ) System.err.print(" SCALE"); + if ((type & TRANSLATION) > 0 ) System.err.print(" TRANSLATION"); + if ((type & ORTHOGONAL) > 0 ) System.err.print(" ORTHOGONAL"); + if ((type & RIGID) > 0 ) System.err.print(" RIGID"); + if ((type & CONGRUENT) > 0 ) System.err.print(" CONGRUENT"); + if ((type & AFFINE) > 0 ) System.err.print(" AFFINE"); + if ((type & NEGATIVE_DETERMINANT) > 0 ) System.err.print(" NEGATIVE_DETERMINANT"); + } + */ + + /** + * Returns the sign of the determinant of this matrix; a return value + * of true indicates a non-negative determinant; a return value of false + * indicates a negative determinant. A value of true will be returned if + * the determinant is NaN. 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 non-negative, false means negative + */ + public final boolean getDeterminantSign() { + double det = determinant(); + if (Double.isNaN(det)) { + return true; + } + return det >= 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; + } + + /** + * 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 + */ + void transform(Point3d point, Point4d 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]; + pointOut.w = mat[12]*point.x + mat[13]*point.y + + mat[14]*point.z + mat[15]; + } + + + + 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 (!isInfOrNaN() && + 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 + */ + @Override + 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 = ALL_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 = ALL_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 = ALL_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 = ALL_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 = ALL_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 = ALL_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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(q1)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(q1)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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]; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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]; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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]; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(q1)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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]; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(q1)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(a1)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(a1)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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]; + } + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(a1)) { + dirtyBits = ALL_DIRTY; + return; + } + + // 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]; + } + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(a1)) { + dirtyBits = ALL_DIRTY; + return; + } + + // 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(angle)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(angle)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(angle)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(trans)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(trans)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(scale)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(scale)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_DIRTY; + } + + /** + * 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(trans)) { + dirtyBits = ALL_DIRTY; + return; + } + + // 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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(trans)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_DIRTY; + } + + /** + * 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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_DIRTY; + } + + /** + * 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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_DIRTY; + } + + /** + * 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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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 = ALL_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 = ALL_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 = ALL_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 = ALL_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 = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(euler)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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 = ALL_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; + + // Issue 253: set all dirty bits if input is infinity or NaN + if (isInfOrNaN(x) || isInfOrNaN(y) || isInfOrNaN(z) || isInfOrNaN(scale)) { + dirtyBits = ALL_DIRTY; + return; + } + + 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 = ALL_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 = ALL_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 = ALL_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 = ALL_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 = new Transform3D(); + t2.autoNormalize = false; + t2.invert(t1); + this.mul(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 = new Transform3D(); + t3.autoNormalize = false; + t3.invert(t2); + this.mul(t1,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 = new Transform3D(); + t3.autoNormalize = false; + t3.transpose(t2); + mul(t1, 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 = new Transform3D(); + t3.autoNormalize = false; + t3.transpose(t1); + mul(t3, t2); + } + + + /** + * 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 = new Transform3D(); + Transform3D t4 = new Transform3D(); + t3.autoNormalize = false; + t4.autoNormalize = false; + t3.transpose(t1); + t4.transpose(t2); + mul(t3, 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() { + // Issue 253: Unable to normalize matrices with infinity or NaN + if (!isAffine() && isInfOrNaN()) { + return; + } + + 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() { + // Issue 253: Unable to normalize matrices with infinity or NaN + if (!isAffine() && isInfOrNaN()) { + return; + } + + 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 + */ + @Override + 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 + */ + @Override + public int hashCode() { + long bits = 1L; + + for (int i = 0; i < 16; i++) { + bits = J3dHash.mixDoubleBits(bits, mat[i]); + } + return J3dHash.finish(bits); + } + + + /** + * 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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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 = ALL_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 = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_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). + *

+ * 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; + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + // Issue 253: set all dirty bits + dirtyBits = ALL_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; + + // Issue 253: set all dirty bits + dirtyBits = ALL_DIRTY; + } + + /** + * 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]; + + + // XXXX: 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.err.println("Optimize!!"); + outScale[0] = outScale[1] = outScale[2] = 1.0; + for(i=0;i<9;i++) + outRot[i] = svdRot[i]; + + return; + } + } + + // XXXX: 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) { + MasterControl.getCoreLogger().severe("outRot == null"); + } + if (rot == null) { + MasterControl.getCoreLogger().severe("rot == null"); + } + + 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 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]; + + // Issue 253: set all dirty bits + t.dirtyBits = ALL_DIRTY; + } + + // somehow CanvasViewCache will directly modify mat[] + // instead of calling ortho(). So we need to reset dirty bit + final void setOrthoDirtyBit() { + // Issue 253: set all dirty bits + dirtyBits = ALL_DIRTY; + type = 0; + } + + // Fix for Issue 167 -- don't classify matrices with Infinity or NaN values + // as affine + private final boolean isInfOrNaN() { + // The following is a faster version of the check. + // Instead of 3 tests per array element (Double.isInfinite is 2 tests), + // for a total of 48 tests, we will do 16 multiplies and 1 test. + double d = 0.0; + for (int i = 0; i < 16; i++) { + d *= mat[i]; + } + + return d != 0.0; + } + + // Fix for Issue 253 + // Methods to check input parameters for Infinity or NaN values + private final boolean isInfOrNaN(Quat4f q) { + return (Float.isNaN(q.x) || Float.isInfinite(q.x) || + Float.isNaN(q.y) || Float.isInfinite(q.y) || + Float.isNaN(q.z) || Float.isInfinite(q.z) || + Float.isNaN(q.w) || Float.isInfinite(q.w)); + } + + private boolean isInfOrNaN(Quat4d q) { + return (Double.isNaN(q.x) || Double.isInfinite(q.x) || + Double.isNaN(q.y) || Double.isInfinite(q.y) || + Double.isNaN(q.z) || Double.isInfinite(q.z) || + Double.isNaN(q.w) || Double.isInfinite(q.w)); + } + + private boolean isInfOrNaN(AxisAngle4f a) { + return (Float.isNaN(a.x) || Float.isInfinite(a.x) || + Float.isNaN(a.y) || Float.isInfinite(a.y) || + Float.isNaN(a.z) || Float.isInfinite(a.z) || + Float.isNaN(a.angle) || Float.isInfinite(a.angle)); + } + + private boolean isInfOrNaN(AxisAngle4d a) { + return (Double.isNaN(a.x) || Double.isInfinite(a.x) || + Double.isNaN(a.y) || Double.isInfinite(a.y) || + Double.isNaN(a.z) || Double.isInfinite(a.z) || + Double.isNaN(a.angle) || Double.isInfinite(a.angle)); + } + + private boolean isInfOrNaN(double val) { + return Double.isNaN(val) || Double.isInfinite(val); + } + + private boolean isInfOrNaN(Vector3f v) { + return (Float.isNaN(v.x) || Float.isInfinite(v.x) || + Float.isNaN(v.y) || Float.isInfinite(v.y) || + Float.isNaN(v.z) || Float.isInfinite(v.z)); + } + + private boolean isInfOrNaN(Vector3d v) { + return (Double.isNaN(v.x) || Double.isInfinite(v.x) || + Double.isNaN(v.y) || Double.isInfinite(v.y) || + Double.isNaN(v.z) || Double.isInfinite(v.z)); + } +} diff --git a/src/javax/media/j3d/TransformGroup.java b/src/javax/media/j3d/TransformGroup.java new file mode 100644 index 0000000..fd9d733 --- /dev/null +++ b/src/javax/media/j3d/TransformGroup.java @@ -0,0 +1,206 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ */ + +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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_TRANSFORM_READ + }; + + /** + * Constructs and initializes a TransformGroup using an + * identity transform. + */ + public TransformGroup() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * 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")); + } + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((TransformGroupRetained)this.retained).setTransform(t1); + } + + /** + * Creates the retained mode TransformGroupRetained object that this + * TransformGroup object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + TransformGroup tg = new TransformGroup(); + tg.duplicateNode(this, forceDuplicate); + return tg; + } + + + /** + * Copies all TransformGroup information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + super.duplicateAttributes(originalNode, forceDuplicate); + + Transform3D t = new Transform3D(); + ((TransformGroupRetained) originalNode.retained).getTransform(t); + ((TransformGroupRetained) retained).setTransform(t); + } +} diff --git a/src/javax/media/j3d/TransformGroupData.java b/src/javax/media/j3d/TransformGroupData.java new file mode 100644 index 0000000..fea8925 --- /dev/null +++ b/src/javax/media/j3d/TransformGroupData.java @@ -0,0 +1,37 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +class TransformGroupData extends NodeData { + // per path node data + // XXXX: replace per path mirror objects with node data + // XXXX: 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/javax/media/j3d/TransformGroupRetained.java b/src/javax/media/j3d/TransformGroupRetained.java new file mode 100644 index 0000000..e7d404d --- /dev/null +++ b/src/javax/media/j3d/TransformGroupRetained.java @@ -0,0 +1,1270 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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, XXXX: 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 = new Transform3D(staticTransform.transform); + trans.mul(t1); + + transform.setWithLock(trans); + + } else { + trans = new Transform3D(t1); + transform.setWithLock(t1); + } + + if (transformChange != null) { + notifyConditions(); + } + + if (source.isLive()) { + + if (aboveAViewPlatform && !t1.isCongruent()) { + throw new BadTransformException(J3dI18N.getString("ViewPlatformRetained0")); + } + + tchangeMessage = new J3dMessage(); + tchangeMessage.type = J3dMessage.TRANSFORM_CHANGED; + tchangeMessage.threads = targetThreads; + tchangeMessage.args[1] = this; + tchangeMessage.args[2] = trans; + + tchangeMessage.universe = universe; + //System.err.println("TransformGroupRetained --- TRANSFORM_CHANGED " + this); + VirtualUniverse.mc.processMessage(tchangeMessage); + } + dirtyBoundsCache(); + } + + /** + * 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 + @Override + 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 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= 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; + } + + @Override + void setAuxData(SetLiveState s, int index, int hkIndex) { + super.setAuxData(s, index, hkIndex); + perPathData[hkIndex] = new TransformGroupData(); + perPathData[hkIndex].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(); + } + } + } + + @Override + boolean isStatic() { + if (!super.isStatic() || + source.getCapability(TransformGroup.ALLOW_TRANSFORM_READ) || + source.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE)) { + return false; + } else { + return true; + } + } + + @Override + void mergeTransform(TransformGroupRetained xform) { + super.mergeTransform(xform); + transform.mul(xform.transform, transform); + } + + @Override + void traverse(boolean sameLevel, int level) { + + System.err.println(); + for (int i = 0; i < level; i++) { + System.err.print("."); + } + System.err.print(this); + + if (isStatic()) { + System.err.print(" (s)"); + } else { + System.err.print(" (w)"); + } + System.err.println(); + System.err.println(transform.toString()); + super.traverse(true, level); + } + + @Override + 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; + } + + @Override + 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. + */ + @Override + void setLive(SetLiveState s) { + int i,j; + Transform3D trans = null; + Targets[] newTargets = null; + Targets[] savedTransformTargets = null; + int oldTraverseFlags = 0; + int len; + Object obj; + + // XXXX - 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 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 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= 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; + MasterControl.getCoreLogger().severe("TG.removeNodeData-Can't find matching hashKey."); + } + } + + 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; + } + } + + @Override + 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 maxTransformLevel) { + maxTransformLevel = transformLevels[i]; + } + } + } else { + maxTransformLevel = -1; + } + + if (s.switchTargets != null) { + for (int i=0; i=0; i--) { + NodeRetained child = children.get(i); + if(child != null) + child.computeCombineBounds(boundingObject); + } + + if (VirtualUniverse.mc.cacheAutoComputedBounds) { + cachedBounds = (Bounds) boundingObject.clone(); + } + } + else { + // Should this be lock too ? ( MT safe ? ) + synchronized(localBounds) { + boundingObject.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) { + boundingObject.transform(transform); + } + bounds.combine(boundingObject); + + } + + 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 dirtyTransformGroups, + ArrayList keySet, + UpdateTargets targets, + ArrayList blUsers) { + + int i, j; + 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.err.println("tg.updateChild skip"); + } + + + + // update child's localToVworld of its children + // transformLink may contain link nodes + synchronized(childTransformLinks) { + for (i=0; i dirtyTransformGroups, + ArrayList keySet, + UpdateTargets targets, + ArrayList blUsers) { + int i, j; + 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.err.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= 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 + // **************************** + + @Override + public int getTargetThreads(int type) { + // type is ignored here, only need for SharedGroup + if (type == TargetsInterface.TRANSFORM_TARGETS) { + return targetThreads; + } else { + System.err.println("getTargetsThreads: wrong arguments"); + return -1; + } + } + + @Override + 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.err.println("getCachedTargets: wrong arguments"); + return null; + } + } + + @Override + TargetsInterface getClosestTargetsInterface(int type) { + return (type == TargetsInterface.TRANSFORM_TARGETS)? + (TargetsInterface)this: + (TargetsInterface)parentSwitchLink; + } + + // re-evalute localTargetThreads using newCachedTargets and + // re-evaluate targetThreads + @Override + 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 getTargetsData(int type, int index) { + // not used + return null; +} + + @Override + 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/javax/media/j3d/TransformInterpolator.java b/src/javax/media/j3d/TransformInterpolator.java new file mode 100644 index 0000000..09b4b95 --- /dev/null +++ b/src/javax/media/j3d/TransformInterpolator.java @@ -0,0 +1,250 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 = 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 + */ + @Override + 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 + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/TransformStructure.java b/src/javax/media/j3d/TransformStructure.java new file mode 100644 index 0000000..1251540 --- /dev/null +++ b/src/javax/media/j3d/TransformStructure.java @@ -0,0 +1,759 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; + +/** + * A transform update is a object that manages TransformGroups + */ + +class TransformStructure extends J3dStructure implements ObjectUpdate { + + /** + * A set of TransformGroups and associated Transform3Ds to traverse + */ + private HashSet transformSet = new HashSet(); + + private ArrayList objectList = new ArrayList(); + + /** + * arraylist of the bounding leaf users affected by the transform + */ + private ArrayList blUsers = new ArrayList(); + + // to gather transform targets + private UpdateTargets targets = new UpdateTargets(); + + /** + * An arrayList of nodes that need collisionBounds updates + */ + private ArrayList collisionObjectList = new ArrayList(); + +// List of dirty TransformGroups +private ArrayList dirtyTransformGroups = new ArrayList(); + + // Associated Keys with the dirtyNodeGroup + private ArrayList keySet = new ArrayList(); + + // the active list contains changed TransformGroup minus those that + // have been switched-off, plus those that have been changed but + // just switched-on + private ArrayList activeTraverseList = + new ArrayList(); + + // contains TG that have been previously changed but just switched-on + private ArrayList switchDirtyTgList = new ArrayList(1); + + private boolean lazyUpdate = false; + + // ArrayList of switches that have changed, use for lastSwitchOn updates + private ArrayList switchChangedList = new ArrayList(); + + // true if already in MasterControl's update object list + private boolean inUpdateObjectList = false; + + /** + * This constructor does nothing + */ + TransformStructure(VirtualUniverse u) { + super(u, J3dThread.UPDATE_TRANSFORM); + } + + @Override + void processMessages(long referenceTime) { + J3dMessage[] messages = getMessages(referenceTime); + int nMsg = getNumMessage(); + J3dMessage m; + int i; + + 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) { + // Add the TG and associated transform. Since this is a + // set, duplicates will be culled. + transformSet.add(new TransformData((TransformGroupRetained)m.args[1], (Transform3D)m.args[2])); + } + } + + for (i=0; i 0) { + processGeometryAtomVwcBounds(); + } + processVwcBounds(); + } + + // Issue 434: clear references to objects that have been processed + objectList.clear(); + + Arrays.fill(messages, 0, nMsg, null); + } + + void processCurrentLocalToVworld() { + int i, j, tSize, sSize; + TransformGroupRetained tg; + BranchGroupRetained bgr; + Transform3D t; + TransformGroupData data; + + lazyUpdate = false; + + tSize = transformSet.size(); + sSize = switchDirtyTgList.size(); + if (tSize <= 0 && sSize <= 0) { + return; + } + + // process TG with setTransform changes + // update Transform3D, switchDirty and lToVwDrity flags + if (tSize > 0) { + Iterator it = transformSet.iterator(); + while(it.hasNext()) { + TransformData lData = it.next(); + tg = lData.getTransformGroupRetained(); + tg.currentTransform.set(lData.getTransform3D()); + + 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 0) { + activeTraverseList.addAll(switchDirtyTgList); + switchDirtyTgList.clear(); + lazyUpdate = true; + } + + // activeTraverseList contains switched-on tg as well + tSize = activeTraverseList.size(); + TransformGroupRetained[] tgs = activeTraverseList.toArray(new TransformGroupRetained[tSize]); + + // process active TGs + if (tSize > 0) { + + sortTransformGroups(tSize, tgs); + + // update lToVw and gather targets + for (i=0; i0 && + (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, TransformGroupRetained[] tgs ) { + int i = l; + int j = r; + double k = tgs[(l+r) / 2].maxTransformLevel; + do { + while (tgs[i].maxTransformLevel 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; + 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++) { + TransformGroupRetained tg = dirtyTransformGroups.get(i); + // Check if the transformGroup is still alive + + // XXXX: 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 0) { + // update SwitchState's CurrentSwitchOn flag + SwitchState switchState; + for (int j=0; j + *

    + *
  • Transparency mode - defines how transparency is applied to + * this Appearance component object:
  • + *

      + *
    • FASTEST - uses the fastest available method for transparency.
    • + *

    • NICEST - uses the nicest available method for transparency.
    • + *

    • 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.
    • + *

    • BLENDED - uses alpha blended transparency. The blend equation is + * specified by the srcBlendFunction and dstBlendFunction attributes. + * The default equation is: + *
        + * alphasrc*src + + * (1-alphasrc)*dst + *
      + * where alphasrc is + * 1-transparency. + * 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: + *
        + * alphasrc = + * alphapix * + * (1-transparency). + *
      + *
    • + *

    • NONE - no transparency; opaque object.
    • + *

    + *
  • Transparency 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.
  • + *

  • 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:
  • + *

      + *
    • BLEND_ZERO - the blend function is f = 0
    • + *
    • BLEND_ONE - the blend function is f = 1
    • + *
    • BLEND_SRC_ALPHA - the blend function is f = + * alphasrc
    • + *
    • BLEND_ONE_MINUS_SRC_ALPHA - the blend function is f = + * 1 - alphasrc
    • + *
    • BLEND_DST_COLOR - the blend function is f = + * colordst
    • + *
    • BLEND_ONE_MINUS_DST_COLOR - the blend function is f = + * 1 - colordst
    • + *
    • BLEND_SRC_COLOR - the blend function is f = + * colorsrc
    • + *
    • BLEND_ONE_MINUS_SRC_COLOR - the blend function is f = + * 1 - colorsrc
    • + *
    + *
+ */ +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: + *
    + * alphasrc*src + + * (1-alphasrc)*dst + *
+ * where alphasrc is + * 1-transparency. + * 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: + *
    + * alphasrc = + * alphapix * + * (1-transparency). + *
+ * + * @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: f = 0. + * @see #setSrcBlendFunction + * @see #setDstBlendFunction + * + * @since Java 3D 1.2 + */ + public static final int BLEND_ZERO = 0; + + /** + * Blend function: f = 1. + * @see #setSrcBlendFunction + * @see #setDstBlendFunction + * + * @since Java 3D 1.2 + */ + public static final int BLEND_ONE = 1; + + /** + * Blend function: + * f = alphasrc. + * @see #setSrcBlendFunction + * @see #setDstBlendFunction + * + * @since Java 3D 1.2 + */ + public static final int BLEND_SRC_ALPHA = 2; + + /** + * Blend function: + * f = 1-alphasrc. + * @see #setSrcBlendFunction + * @see #setDstBlendFunction + * + * @since Java 3D 1.2 + */ + public static final int BLEND_ONE_MINUS_SRC_ALPHA = 3; + + /** + * Blend function: + * f = colordst. + *

Note that this function may only be used as a source + * blend function.

+ * @see #setSrcBlendFunction + * + * @since Java 3D 1.4 + */ + public static final int BLEND_DST_COLOR = 4; + + /** + * Blend function: + * f = 1-colordst. + *

Note that this function may only be used as a source + * blend function.

+ * @see #setSrcBlendFunction + * + * @since Java 3D 1.4 + */ + public static final int BLEND_ONE_MINUS_DST_COLOR = 5; + + /** + * Blend function: + * f = colorsrc. + *

Note that this function may only be used as a destination + * blend function.

+ * @see #setDstBlendFunction + * + * @since Java 3D 1.4 + */ + public static final int BLEND_SRC_COLOR = 6; + + /** + * Blend function: + * f = 1-colorsrc. + *

Note that this function may only be used as a destination + * blend function.

+ * @see #setDstBlendFunction + * + * @since Java 3D 1.4 + */ + public static final int BLEND_ONE_MINUS_SRC_COLOR = 7; + + static final int BLEND_CONSTANT_COLOR = 8; + + static final int MAX_BLEND_FUNC_TABLE_SIZE = 9; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_BLEND_FUNCTION_READ, + ALLOW_MODE_READ, + ALLOW_VALUE_READ + }; + + /** + * Constructs a TransparencyAttributes object with default parameters. + * The default values are as follows: + *
    + * transparency mode : NONE
    + * transparency value : 0.0
    + * source blend function : BLEND_SRC_ALPHA
    + * destination blend function : BLEND_ONE_MINUS_SRC_ALPHA
    + *
+ */ + public TransparencyAttributes() { + // Just use the default for all attributes + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Construct TransparencyAttributes object with specified values. + * @param tMode the transparency mode + * @param tVal the transparency value + * @exception IllegalArgumentException if + * tMode is a value other than + * NONE, FASTEST, NICEST, + * SCREEN_DOOR, or BLENDED + * + */ + 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 BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, + * BLEND_DST_COLOR, or BLEND_ONE_MINUS_DST_COLOR. + * @param dstBlendFunction the blend function to be used for the + * destination + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, + * BLEND_SRC_COLOR, or BLEND_ONE_MINUS_SRC_COLOR. + * @exception IllegalArgumentException if + * tMode is a value other than + * NONE, FASTEST, NICEST, + * SCREEN_DOOR, or BLENDED + * @exception IllegalArgumentException if + * srcBlendFunction or dstBlendFunction + * is a value other than one of the supported functions listed above. + * + * @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")); + } + + switch (srcBlendFunction) { + case BLEND_ZERO: + case BLEND_ONE: + case BLEND_SRC_ALPHA: + case BLEND_ONE_MINUS_SRC_ALPHA: + case BLEND_DST_COLOR: + case BLEND_ONE_MINUS_DST_COLOR: + break; + default: + throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7")); + } + + switch (dstBlendFunction) { + case BLEND_ZERO: + case BLEND_ONE: + case BLEND_SRC_ALPHA: + case BLEND_ONE_MINUS_SRC_ALPHA: + case BLEND_SRC_COLOR: + case BLEND_ONE_MINUS_SRC_COLOR: + break; + default: + throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8")); + } + + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + + ((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 + * NONE, FASTEST, NICEST, + * SCREEN_DOOR, or BLENDED + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * @exception IllegalArgumentException if + * transparencyMode is a value other than + * NONE, FASTEST, NICEST, + * SCREEN_DOOR, or BLENDED + */ + 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 + * BLEND_SRC_ALPHA. + * + * @param blendFunction the blend function to be used for the source + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, + * BLEND_DST_COLOR, or BLEND_ONE_MINUS_DST_COLOR. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * @exception IllegalArgumentException if blendFunction + * is a value other than one of the supported functions listed above. + * + * @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")); + + switch (blendFunction) { + case BLEND_ZERO: + case BLEND_ONE: + case BLEND_SRC_ALPHA: + case BLEND_ONE_MINUS_SRC_ALPHA: + case BLEND_DST_COLOR: + case BLEND_ONE_MINUS_DST_COLOR: + break; + default: + 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 + * BLEND_ONE_MINUS_SRC_ALPHA. + * + * @param blendFunction the blend function to be used for the destination + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, + * BLEND_SRC_COLOR, or BLEND_ONE_MINUS_SRC_COLOR. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + * @exception IllegalArgumentException if blendFunction + * is a value other than one of the supported functions listed above. + * + * @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")); + + switch (blendFunction) { + case BLEND_ZERO: + case BLEND_ONE: + case BLEND_SRC_ALPHA: + case BLEND_ONE_MINUS_SRC_ALPHA: + case BLEND_SRC_COLOR: + case BLEND_ONE_MINUS_SRC_COLOR: + break; + default: + 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. + */ + @Override + void createRetained() { + this.retained = new TransparencyAttributesRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + TransparencyAttributes transa = new TransparencyAttributes(); + transa.duplicateNodeComponent(this); + return transa; + } + + + + /** + * Copies all node information from originalNodeComponent into + * the current node. This method is called from the + * duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + 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/javax/media/j3d/TransparencyAttributesRetained.java b/src/javax/media/j3d/TransparencyAttributesRetained.java new file mode 100644 index 0000000..5d531a8 --- /dev/null +++ b/src/javax/media/j3d/TransparencyAttributesRetained.java @@ -0,0 +1,353 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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; + + /** + * Sets the transparency mode for this + * appearance component object. + * @param transparencyMode the transparency mode to be used, one of + * NONE, FASTEST, NICEST, + * SCREEN_DOOR, or BLENDED + */ + 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 + * FASTEST, NICEST, + * SCREEN_DOOR, or BLENDED + */ + 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 + * BLEND_SRC_ALPHA. + * + * @param blendFunction the blend function to be used for the source + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, or BLEND_ONE_MINUS_SRC_ALPHA. + */ + 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 + * BLEND_SRC_ALPHA. + * + * @param blendFunction the blend function to be used for the source + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, or BLEND_ONE_MINUS_SRC_ALPHA. + */ + 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 + * BLEND_ONE_MINUS_SRC_ALPHA. + * + * @param blendFunction the blend function to be used for the destination + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, or BLEND_ONE_MINUS_SRC_ALPHA. + * + */ + 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 + * BLEND_ONE_MINUS_SRC_ALPHA. + * + * @param blendFunction the blend function to be used for the destination + * color, one of BLEND_ZERO, BLEND_ONE, + * BLEND_SRC_ALPHA, or BLEND_ONE_MINUS_SRC_ALPHA. + */ + 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 + */ + @Override + 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); + } + } + +static boolean useAlpha(TransparencyAttributesRetained ta) { + if (ta != null && + ta.transparencyMode != TransparencyAttributes.NONE && + ta.transparencyMode != TransparencyAttributes.SCREEN_DOOR) { + return true; + } + return false; +} + + void updateNative(Context ctx, + float alpha, int geometryType, int polygonMode, + boolean lineAA, + boolean pointAA) { + Pipeline.getPipeline().updateTransparencyAttributes(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 + */ + @Override + synchronized void initMirrorObject() { + ((TransparencyAttributesRetained)mirror).set(this); + } + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + 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 = new J3dMessage(); + 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.err.println("univList.size is " + univList.size()); + for(int i=0; i gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + @Override + 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/javax/media/j3d/TransparencyInterpolator.java b/src/javax/media/j3d/TransparencyInterpolator.java new file mode 100644 index 0000000..633bb97 --- /dev/null +++ b/src/javax/media/j3d/TransparencyInterpolator.java @@ -0,0 +1,288 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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). + *

+ * 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.

+ * + * @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 = 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 + */ + @Override + 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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + TransparencyInterpolator ti = new TransparencyInterpolator(); + ti.duplicateNode(this, forceDuplicate); + return ti; + } + + + /** + * Copies all TransparencyInterpolator information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + 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 cloneTree. + * This method is called by cloneTree 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 getNewObjectReference method found in the + * NodeReferenceTable 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 + * allowDanglingReferences parameter passed in the + * cloneTree call. + *

+ * NOTE: Applications should not call this method directly. + * It should only be called by the cloneTree method. + * + * @param referenceTable a NodeReferenceTableObject that contains the + * getNewObjectReference method needed to search for + * new object instances. + * @see NodeReferenceTable + * @see Node#cloneTree + * @see DanglingReferenceException + */ + @Override + 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/javax/media/j3d/TransparencySortGeom.java b/src/javax/media/j3d/TransparencySortGeom.java new file mode 100644 index 0000000..f07a947 --- /dev/null +++ b/src/javax/media/j3d/TransparencySortGeom.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Harvey Harrison + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + */ +package javax.media.j3d; + +/** + * An interface that allows sorting the rendering order of transparent geometry. + * + * @since Java 3D 1.6 + */ +public interface TransparencySortGeom { + +/** + * Returns the Geometry for this object. + * @return geometry for this object + */ +public Geometry getGeometry(); + +/** + * Returns the distance squared of this object to the viewer. + * @return distancesquared to viewer + */ +public double getDistanceSquared(); + +/** + * Returns the LocalToVWorld transform for this object + * @param localToVW variable in which transform will be returned + */ +public void getLocalToVWorld(Transform3D localToVW); + +/** + * Returns the Shape3D being rendered using this geometry. + * @return the Shape3D being rendered using this geometry + */ +public Shape3D getShape3D(); + +} diff --git a/src/javax/media/j3d/TransparencySortMap.java b/src/javax/media/j3d/TransparencySortMap.java new file mode 100644 index 0000000..7f13d0a --- /dev/null +++ b/src/javax/media/j3d/TransparencySortMap.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013 Harvey Harrison + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + */ +package javax.media.j3d; + +import java.util.Comparator; +import java.util.WeakHashMap; + +/** + * A class mapping Views to a TransparencySortGeom object. + */ +public class TransparencySortMap { + +private static final WeakHashMap> comparators = new WeakHashMap>(); + +// prevent an instance from being created. +private TransparencySortMap() {} + +/** + * Set the comparator for the specified view. + * @param view the view to which the comparator applies + * @param comparator the comparator to call + */ +public static void setComparator(View view, Comparator comparator) { + comparators.put(view, comparator); +} + +/** + * Returns the comparator for the specified view + * @return the comparator for the specified view, or null if there is no + * comparator for the view or the view is unknown. + */ +public static Comparator getComparator(View view) { + return comparators.get(view); +} +} diff --git a/src/javax/media/j3d/TransparentRenderingInfo.java b/src/javax/media/j3d/TransparentRenderingInfo.java new file mode 100644 index 0000000..3901bc9 --- /dev/null +++ b/src/javax/media/j3d/TransparentRenderingInfo.java @@ -0,0 +1,163 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +class TransparentRenderingInfo extends Object implements TransparencySortGeom { + // 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; + GeometryAtom geometryAtom; + double zVal; // Used in DepthSorted Transparency + // XXXX: Add Dirty info + + /** + * update state before rendering transparent objects + */ + boolean updateState(Canvas3D cv) { + + TextureBin textureBin = rm.textureBin; + AttributeBin attributeBin = textureBin.attributeBin; + ShaderBin shaderBin = textureBin.shaderBin; + + // 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; + } + + // XXXX : Code cleanup needed : The following code segment should simply test + // each bin independently and update it if necessary. + 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; + } + + // Fix to issue 314. Set the appropriate bits for the dirty bins + // and call the update state method. + cv.setStateToUpdate(Canvas3D.LIGHTBIN_BIT, attributeBin.environmentSet.lightBin); + cv.setStateToUpdate(Canvas3D.ENVIRONMENTSET_BIT, attributeBin.environmentSet); + cv.setStateToUpdate(Canvas3D.ATTRIBUTEBIN_BIT, attributeBin); + cv.setStateToUpdate(Canvas3D.SHADERBIN_BIT, shaderBin); + cv.updateEnvState(); + + } 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; + } + + // Fix to issue 314. Set the appropriate bits for the dirty bins + // and call the update state method. + cv.setStateToUpdate(Canvas3D.ATTRIBUTEBIN_BIT, attributeBin); + cv.setStateToUpdate(Canvas3D.SHADERBIN_BIT, shaderBin); + cv.updateEnvState(); + + } else if (cv.shaderBin != shaderBin) { + + // Fix to issue 314. Set the appropriate bits for the dirty bins + // and call the update state method. + cv.setStateToUpdate(Canvas3D.SHADERBIN_BIT, shaderBin); + cv.updateEnvState(); + + } + + 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); + } + } + + @Override + public double getDistanceSquared() { + return zVal; + } + + @Override + public Geometry getGeometry() { + // XXXX: verify 0 is always the correct index. Assumption is that for + // Shape3D with multiple geometry each geometry is put in it's + // own geometryAtom. + if (geometryAtom.geometryArray[0]==null) + return null; + return (Geometry)geometryAtom.geometryArray[0].source; + } + + @Override + public void getLocalToVWorld(Transform3D localToVW) { + localToVW.set(rm.localToVworld[NodeRetained.LAST_LOCAL_TO_VWORLD]); + } + + @Override + public Shape3D getShape3D() { + return (Shape3D)geometryAtom.source.source; + } +} diff --git a/src/javax/media/j3d/TriangleArray.java b/src/javax/media/j3d/TriangleArray.java new file mode 100644 index 0000000..f1cfb9f --- /dev/null +++ b/src/javax/media/j3d/TriangleArray.java @@ -0,0 +1,191 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or vertexCount is not a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int)} + * for more exceptions that can be thrown + */ + 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 using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or vertexCount is not a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty TriangleArray object using the specified + * parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or vertexCount is not a multiple of 3 + * ;
+ * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public TriangleArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes); + + 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. + */ + @Override + void createRetained() { + this.retained = new TriangleArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + TriangleArrayRetained rt = (TriangleArrayRetained) retained; + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + TriangleArray t = new TriangleArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes); + t.duplicateNodeComponent(this); + return t; + } +} diff --git a/src/javax/media/j3d/TriangleArrayRetained.java b/src/javax/media/j3d/TriangleArrayRetained.java new file mode 100644 index 0000000..810ea7d --- /dev/null +++ b/src/javax/media/j3d/TriangleArrayRetained.java @@ -0,0 +1,491 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + Point3d pnts[] = new Point3d[3]; + double sdist[] = new double[1]; + double minDist = Double.MAX_VALUE; + double x = 0, y = 0, z = 0; + int[] vtxIndexArr = new int[3]; + + 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) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < validVertexCount) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectBoundingPolytope(pnts, bpolytope, + sdist,iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + while (i < validVertexCount) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectCylinder(pnts, pickCylinder, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < validVertexCount) { + for(int j=0; j<3; j++) { + vtxIndexArr[j] = i; + getVertexData(i++, pnts[j]); + } + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + } + 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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + + @Override + 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 + * See {@link GeometryStripArray#GeometryStripArray(int,int,int[])} + * for more exceptions that can be thrown + */ + 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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or any element in the stripVertexCounts array is less than 3 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty TriangleFanArray object using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or any element in the stripVertexCounts array is less than 3 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public TriangleFanArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int[] stripVertexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + stripVertexCounts); + + if (vertexCount < 3 ) + throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArray0")); + } + + /** + * Creates the retained mode TriangleFanArrayRetained object that this + * TriangleFanArray object will point to. + */ + @Override + void createRetained() { + this.retained = new TriangleFanArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + TriangleFanArrayRetained rt = (TriangleFanArrayRetained) retained; + int stripcounts[] = new int[rt.getNumStrips()]; + rt.getStripVertexCounts(stripcounts); + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + TriangleFanArray t = new TriangleFanArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + stripcounts); + t.duplicateNodeComponent(this); + return t; + } + +} diff --git a/src/javax/media/j3d/TriangleFanArrayRetained.java b/src/javax/media/j3d/TriangleFanArrayRetained.java new file mode 100644 index 0000000..373a62b --- /dev/null +++ b/src/javax/media/j3d/TriangleFanArrayRetained.java @@ -0,0 +1,597 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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(); + int[] vtxIndexArr = new int[3]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectBoundingSphere(pnts, bsphere, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectBoundingPolytope(pnts, bpolytope, + sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + // intersect pnts[] with every triangle in this object + @Override + 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; + } + + @Override + 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 + @Override + 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 + @Override + void computeCentroid() { + Vector3d vec = new Vector3d(); + Vector3d normal = new Vector3d(); + Vector3d tmpvec = new Vector3d(); + Point3d pnt0 = new Point3d(); + Point3d pnt1 = new Point3d(); + Point3d pnt2 = new Point3d(); + 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; + } + } + + @Override + int getClassType() { + return TRIANGLE_TYPE; + } +} diff --git a/src/javax/media/j3d/TriangleStripArray.java b/src/javax/media/j3d/TriangleStripArray.java new file mode 100644 index 0000000..ebe9755 --- /dev/null +++ b/src/javax/media/j3d/TriangleStripArray.java @@ -0,0 +1,217 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int)} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or any element in the stripVertexCounts array is less than 3 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int[])} + * for more exceptions that can be thrown + */ + 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 using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[])} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or any element in the stripVertexCounts array is less than 3 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])} + * for more exceptions that can be thrown + * + * @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")); + } + + /** + * Constructs an empty TriangleStripArray object using the + * specified parameters. + * + * @param vertexCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexFormat + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param texCoordSetMap + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrCount + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param vertexAttrSizes + * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])} + * for a description of this parameter. + * + * @param stripVertexCounts + * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])} + * for a description of this parameter. + * + * @exception IllegalArgumentException if vertexCount is less than 3 + * or any element in the stripVertexCounts array is less than 3 + * ;
+ * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])} + * for more exceptions that can be thrown + * + * @since Java 3D 1.4 + */ + public TriangleStripArray(int vertexCount, + int vertexFormat, + int texCoordSetCount, + int[] texCoordSetMap, + int vertexAttrCount, + int[] vertexAttrSizes, + int[] stripVertexCounts) { + + super(vertexCount, vertexFormat, + texCoordSetCount, texCoordSetMap, + vertexAttrCount, vertexAttrSizes, + stripVertexCounts); + + if (vertexCount < 3 ) + throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArray0")); + } + + /** + * Creates the retained mode TriangleStripArrayRetained object that this + * TriangleStripArray object will point to. + */ + @Override + void createRetained() { + this.retained = new TriangleStripArrayRetained(); + this.retained.setSource(this); + } + + + /** + * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate) + */ + @Override + public NodeComponent cloneNodeComponent() { + TriangleStripArrayRetained rt = (TriangleStripArrayRetained) retained; + int stripcounts[] = new int[rt.getNumStrips()]; + rt.getStripVertexCounts(stripcounts); + int texSetCount = rt.getTexCoordSetCount(); + int[] texMap = null; + int vertexAttrCount = rt.getVertexAttrCount(); + int[] vertexAttrSizes = null; + if (texSetCount > 0) { + texMap = new int[rt.getTexCoordSetMapLength()]; + rt.getTexCoordSetMap(texMap); + } + if (vertexAttrCount > 0) { + vertexAttrSizes = new int[vertexAttrCount]; + rt.getVertexAttrSizes(vertexAttrSizes); + } + TriangleStripArray t = new TriangleStripArray(rt.getVertexCount(), + rt.getVertexFormat(), + texSetCount, + texMap, + vertexAttrCount, + vertexAttrSizes, + stripcounts); + t.duplicateNodeComponent(this); + return t; + } +} diff --git a/src/javax/media/j3d/TriangleStripArrayRetained.java b/src/javax/media/j3d/TriangleStripArrayRetained.java new file mode 100644 index 0000000..c1da285 --- /dev/null +++ b/src/javax/media/j3d/TriangleStripArrayRetained.java @@ -0,0 +1,623 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * 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; + } + + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + 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(); + int[] vtxIndexArr = new int[3]; + + switch (pickShape.getPickType()) { + case PickShape.PICKRAY: + PickRay pickRay= (PickRay) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectRay(pnts, pickRay, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKSEGMENT: + PickSegment pickSegment = (PickSegment) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectSegment(pnts, pickSegment.start, + pickSegment.end, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGBOX: + BoundingBox bbox = (BoundingBox) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGSPHERE: + BoundingSphere bsphere = (BoundingSphere) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectBoundingSphere(pnts, bsphere, sdist, + iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKBOUNDINGPOLYTOPE: + BoundingPolytope bpolytope = (BoundingPolytope) + ((PickBounds) pickShape).bounds; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectBoundingPolytope(pnts, bpolytope, + sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCYLINDER: + PickCylinder pickCylinder= (PickCylinder) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[2]; + } + } + break; + case PickShape.PICKCONE: + PickCone pickCone= (PickCone) pickShape; + + while (i < stripVertexCounts.length) { + j = stripStartVertexIndices[i]; + end = j + stripVertexCounts[i++]; + for(int k=0; k<2; k++) { + vtxIndexArr[k] = j; + getVertexData(j++, pnts[k]); + } + while (j < end) { + vtxIndexArr[2] = j; + getVertexData(j++, pnts[2]); + if (intersectCone(pnts, pickCone, sdist, iPnt)) { + if (flags == 0) { + return true; + } + if (sdist[0] < minDist) { + minDist = sdist[0]; + x = iPnt.x; + y = iPnt.y; + z = iPnt.z; + if((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + if((flags & PickInfo.ALL_GEOM_INFO) != 0) { + storeInterestData(pickInfo, flags, geom, geomIndex, + vtxIndexArr, iPnt, sdist[0]); + } + } + pnts[0].set(pnts[1]); + vtxIndexArr[0] = vtxIndexArr[1]; + pnts[1].set(pnts[2]); + vtxIndexArr[1] = vtxIndexArr[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) { + iPnt.x = x; + iPnt.y = y; + iPnt.z = z; + return true; + } + return false; + } + + // intersect pnts[] with every triangle in this object + @Override + 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; + } + + @Override + 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 + @Override + 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 + @Override + void computeCentroid() { + Point3d pnt0 = new Point3d(); + Point3d pnt1 = new Point3d(); + Point3d pnt2 = new Point3d(); + Vector3d vec = new Vector3d(); + Vector3d normal = new Vector3d(); + Vector3d tmpvec = new Vector3d(); + + 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; + } + } + + + @Override + int getClassType() { + return TRIANGLE_TYPE; + } +} diff --git a/src/javax/media/j3d/UnorderList.java b/src/javax/media/j3d/UnorderList.java new file mode 100644 index 0000000..3d8cdd8 --- /dev/null +++ b/src/javax/media/j3d/UnorderList.java @@ -0,0 +1,590 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * Use the following code to iterate through an array. + * + *

+ *  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];
+ *      ....
+ *  }
+ * 
+ * + *

+ * Note: + *

    + * 1) The array return is a copied of internal array.
    + * 2) Don't use arr.length , use list.arraySize();
    + * 3) No need to do casting for individual element as in + * ArrayList.
    + * 4) UnorderList is thread safe. + *
+ */ + +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 true if this list has no elements; + * false otherwise. + */ + final boolean isEmpty() { + return size == 0; + } + + /** + * Returns true 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 equals method. + * + * @param o an object. + * @return the index of the first occurrence of the argument in this + * list; returns -1 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 ArrayList instance. (The + * elements themselves are not copied.) + * + * @return a clone of this ArrayList instance. + */ + @Override + 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 ArrayList instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an ArrayList 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 (index + * < 0 || index >= size()). + */ + 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 + * (index < 0 || index >= size()). + */ + 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 (index + * < 0 || index >= size()). + */ + 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 (index + * < 0 || index >= size()). + */ + 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 (index + * < 0 || index >= size()). + */ + 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; + } + + @Override + 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 ArrayList instance to a stream (that + * is, serialize it). + * + * @serialData The length of the array backing the ArrayList + * instance is emitted (int), followed by all of its elements + * (each an Object) 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; iArrayList 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 + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + */ +package javax.media.j3d; + +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * A small utility class for internal use. Mainly contains some distance-calculation + * methods. + * + */ +class Utils { + +/** + * Returns the square of the minimum distance from the given point to the segment + * defined by start, end. + */ +static final double ptToSegSquare(Point3d pt, Point3d start, Point3d end, Point3d closest) { + Vector3d dir = new Vector3d(); + dir.sub(end, start); + + Vector3d dt = new Vector3d(); + dt.sub(pt, start); + + // Project the point onto the line defined by the segment + double proj = dir.dot(dt); + + // We projected 'before' the start point, just return the dSquared between + // the point and the start + if (proj <= 0.0d) { + if (closest != null) closest.set(start); + return dt.lengthSquared(); + } + + // Project the segment onto itself + double segSquared = dir.lengthSquared(); + + // If our point projected off the end of the segment, return the dSquared between + // the point and the end + if (proj >= segSquared) { + if (closest != null) closest.set(end); + dt.sub(pt, end); + return dt.lengthSquared(); + } + + // We projected somewhere along the segment, calculate the closest point + dt.scaleAdd(proj / segSquared, dir, start); + if (closest != null) closest.set(dt); + + // return the distance from the point to the closest point on the segment + dt.sub(pt, dt); + return dt.lengthSquared(); +} + +/** + * Returns the square of the minimum distance from the given point to the ray + * defined by start, dir. + */ +static final double ptToRaySquare(Point3d pt, Point3d start, Vector3d dir, Point3d closest) { + Vector3d dt = new Vector3d(); + dt.sub(pt, start); + + // Project the point onto the ray + double proj = dir.dot(dt); + + // We projected 'before' the start point, just return the dSquared between + // the point and the start + if (proj <= 0.0d) { + if (closest != null) closest.set(start); + return dt.lengthSquared(); + } + + // Project the ray onto itself + double raySquared = dir.lengthSquared(); + + // We projected somewhere along the ray, calculate the closest point + dt.scaleAdd(proj / raySquared, dir, start); + if (closest != null) closest.set(dt); + + // return the distance from the point to the closest point on the ray + dt.sub(pt, dt); + return dt.lengthSquared(); +} + +private static final double ZERO_TOL = 1e-5d; + +/** + * Return the square of the minimum distance between a ray and a segment. + * Geometric Tools, LLC + * Copyright (c) 1998-2012 + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt + * http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp + * File Version: 5.0.1 (2010/10/01) + */ +static public double rayToSegment(Point3d rayorig, Vector3d raydir, + Point3d segstart, Point3d segend, + Point3d rayint, Point3d segint, double[] param) { + double s, t; + + Vector3d diff = new Vector3d(); + diff.sub(rayorig, segstart); + Vector3d segdir = new Vector3d(); + segdir.sub(segend, segstart); + + double A = raydir.dot(raydir);// Dot(ray.m,ray.m); + double B = -raydir.dot(segdir);// -Dot(ray.m,seg.m); + double C = segdir.dot(segdir);// Dot(seg.m,seg.m); + double D = raydir.dot(diff);// Dot(ray.m,diff); + double E; // -Dot(seg.m,diff), defer until needed + double F = diff.dot(diff);// Dot(diff,diff); + double det = Math.abs(A * C - B * B); // A*C-B*B = |Cross(M0,M1)|^2 >= 0 + + double tmp; + + if (det >= ZERO_TOL) { + // ray and segment are not parallel + E = -segdir.dot(diff);// -Dot(seg.m,diff); + s = B * E - C * D; + t = B * D - A * E; + + if (s >= 0) { + if (t >= 0) { + if (t <= det) { // region 0 + // minimum at interior points of ray and segment + double invDet = 1.0f / det; + s *= invDet; + t *= invDet; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.scaleAdd(t, segdir, segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(s * (A * s + B * t + 2 * D) + t + * (B * s + C * t + 2 * E) + F); + } + else { // region 1 + + t = 1; + if (D >= 0) { + s = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + s = -D / A; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs((D + 2 * B) * s + C + 2 * E + F); + } + } + } + else { // region 5 + t = 0; + if (D >= 0) { + s = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else { + s = -D / A; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + } + } + else { + if (t <= 0) { // region 4 + if (D < 0) { + s = -D / A; + t = 0; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + else { + s = 0; + if (E >= 0) { + t = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-E >= C) { + t = 1; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -E / C; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.scaleAdd(t, segdir, segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(E * t + F); + } + } + } + else if (t <= det) { // region 3 + s = 0; + if (E >= 0) { + t = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-E >= C) { + t = 1; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -E / C; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.scaleAdd(t, segdir, segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(E * t + F); + } + } + else { // region 2 + tmp = B + D; + if (tmp < 0) { + s = -tmp / A; + t = 1; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * s + C + 2 * E + F); + } + else { + s = 0; + if (E >= 0) { + t = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-E >= C) { + t = 1; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -E / C; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.scaleAdd(t, segdir, segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(E * t + F); + } + } + } + } + } + else { + // ray and segment are parallel + if (B > 0) { + // opposite direction vectors + t = 0; + if (D >= 0) { + s = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else { + s = -D / A; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.set(segstart); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + } + else { + // same direction vectors + E = segdir.dot(diff);// -Dot(seg.m,diff); + t = 1; + tmp = B + D; + if (tmp >= 0) { + s = 0; + if (rayint != null) rayint.set(rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + s = -tmp / A; + if (rayint != null) rayint.scaleAdd(s, raydir, rayorig); + if (segint != null) segint.set(segend); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * s + C + 2 * E + F); + } + } + } +} + +/** + * Return the square of the minimum distance between two line segments. + * + * Code in this method adapted from: + * Geometric Tools, LLC + * Copyright (c) 1998-2012 + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt + * http://www.geometrictools.com/LibMathematics/Distance/Wm5DistSegment3Segment3.cpp + * File Version: 5.0.1 (2010/10/01) + */ +static public double segmentToSegment(Point3d s0start, Point3d s0end, + Point3d s1start, Point3d s1end, + Point3d s0int, Point3d s1int, double[] param) { + double s, t; + + Vector3d diff = new Vector3d(); + diff.sub(s0start, s1start); + + Vector3d seg0dir = new Vector3d(); + seg0dir.sub(s0end, s0start); + Vector3d seg1dir = new Vector3d(); + seg1dir.sub(s1end, s1start); + + double A = seg0dir.dot(seg0dir); // Dot(seg0dir,seg0dir); + double B = -seg0dir.dot(seg1dir); // -Dot(seg0dir,seg1dir); + double C = seg1dir.dot(seg1dir); // Dot(seg1dir,seg1dir); + double D = seg0dir.dot(diff); // Dot(seg0dir,diff); + double E; // -Dot(seg1dir,diff), defer until needed + double F = diff.dot(diff); // Dot(diff,diff); + double det = Math.abs(A * C - B * B); // A*C-B*B = |Cross(M0,M1)|^2 >= 0 + + double tmp; + + if (det >= ZERO_TOL) { + // line segments are not parallel + E = -seg1dir.dot(diff); // -Dot(seg1dir,diff); + s = B * E - C * D; + t = B * D - A * E; + + if (s >= 0) { + if (s <= det) { + if (t >= 0) { + if (t <= det) { // region 0 (interior) + // minimum at two interior points of 3D lines + double invDet = 1.0f / det; + s *= invDet; + t *= invDet; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(s * (A * s + B * t + 2 * D) + t + * (B * s + C * t + 2 * E) + F); + } + else { // region 3 (side) + t = 1; + tmp = B + D; + if (tmp >= 0) { + s = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else if (-tmp >= A) { + s = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + C + F + 2 * (E + tmp)); + } + else { + s = -tmp / A; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * s + C + 2 * E + F); + } + } + } + else { // region 7 (side) + t = 0; + if (D >= 0) { + s = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-D >= A) { + s = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F); + } + else { + s = -D / A; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + } + } + else { + if (t >= 0) { + if (t <= det) { // region 1 (side) + s = 1; + tmp = B + E; + if (tmp >= 0) { + t = 0; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F); + } + else if (-tmp >= C) { + t = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + C + F + 2 * (D + tmp)); + } + else { + t = -tmp / C; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * t + A + 2 * D + F); + } + } + else { // region 2 (corner) + tmp = B + D; + if (-tmp <= A) { + t = 1; + if (tmp >= 0) { + s = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + s = -tmp / A; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * s + C + 2 * E + F); + } + } + else { + s = 1; + tmp = B + E; + if (tmp >= 0) { + t = 0; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F); + } + else if (-tmp >= C) { + t = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + C + F + 2 * (D + tmp)); + } + else { + t = -tmp / C; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * t + A + 2 * D + F); + } + } + } + } + else { // region 8 (corner) + if (-D < A) { + t = 0; + if (D >= 0) { + s = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else { + s = -D / A; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + } + else { + s = 1; + tmp = B + E; + if (tmp >= 0) { + t = 0; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F); + } + else if (-tmp >= C) { + t = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + C + F + 2 * (D + tmp)); + } + else { + t = -tmp / C; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * t + A + 2 * D + F); + } + } + } + } + } + else { + if (t >= 0) { + if (t <= det) { // region 5 (side) + s = 0; + if (E >= 0) { + t = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-E >= C) { + t = 1; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -E / C; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(E * t + F); + } + } + else { // region 4 (corner) + tmp = B + D; + if (tmp < 0) { + t = 1; + if (-tmp >= A) { + s = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + C + F + 2 * (E + tmp)); + } + else { + s = -tmp / A; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(tmp * s + C + 2 * E + F); + } + } + else { + s = 0; + if (E >= 0) { + t = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-E >= C) { + t = 1; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -E / C; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(E * t + F); + } + } + } + } + else { // region 6 (corner) + if (D < 0) { + t = 0; + if (-D >= A) { + s = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F); + } + else { + s = -D / A; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + } + else { + s = 0; + if (E >= 0) { + t = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-E >= C) { + t = 1; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -E / C; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(E * t + F); + } + } + } + } + } + else { + // line segments are parallel + if (B > 0) { + // direction vectors form an obtuse angle + if (D >= 0) { + s = 0; + t = 0; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F); + } + else if (-D <= A) { + s = -D / A; + t = 0; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + else { + E = -seg1dir.dot(diff); // -Dot(seg1dir,diff); + s = 1; + tmp = A + D; + if (-tmp >= B) { + t = 1; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + C + F + 2 * (B + D + E)); + } + else { + t = -tmp / B; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F + t * (C * t + 2 * (B + E))); + } + } + } + else { + // direction vectors form an acute angle + if (-D >= A) { + s = 1; + t = 0; + if (s0int != null) s0int.set(s0end); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(A + 2 * D + F); + } + else if (D <= 0) { + s = -D / A; + t = 0; + if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start); + if (s1int != null) s1int.set(s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(D * s + F); + } + else { + E = -seg1dir.dot(diff); // -Dot(seg1dir,diff); + s = 0; + if (D >= -B) { + t = 1; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.set(s1end); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(C + 2 * E + F); + } + else { + t = -D / B; + if (s0int != null) s0int.set(s0start); + if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start); + if (param != null) { param[0] = s; param[1] = t; } + return Math.abs(F + t * (2 * E + C * t)); + } + } + } + } +} +} diff --git a/src/javax/media/j3d/VertexArrayRenderMethod.java b/src/javax/media/j3d/VertexArrayRenderMethod.java new file mode 100644 index 0000000..bdf937b --- /dev/null +++ b/src/javax/media/j3d/VertexArrayRenderMethod.java @@ -0,0 +1,98 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * The RenderMethod interface is used to create various ways to render + * different geometries. + */ + +class VertexArrayRenderMethod implements RenderMethod { + + + @Override + public boolean render(RenderMolecule rm, Canvas3D cv, + 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(dirtyBits); + while (ra != null) { + renderGeo(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(dirtyBits); + renderGeo(ra, rm, cv); + isVisible = true; + } + } + else { + if (!VirtualUniverse.mc.viewFrustumCulling || + ra.renderAtom.localeVwcBounds.intersect(cv.viewFrustum)) { + cv.updateState(dirtyBits); + cv.raIsVisible = true; + renderGeo(ra, rm, cv); + isVisible = true; + } + else { + cv.raIsVisible = false; + } + cv.ra = ra.renderAtom; + } + + ra = ra.next; + } + return isVisible; + } + + void renderGeo(RenderAtomListInfo ra, RenderMolecule rm, 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, + cv.screen.screen, + rm.textureBin.attributeBin.ignoreVertexColors); + } +} diff --git a/src/javax/media/j3d/View.java b/src/javax/media/j3d/View.java new file mode 100644 index 0000000..d162ecd --- /dev/null +++ b/src/javax/media/j3d/View.java @@ -0,0 +1,3336 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.AWTEvent; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Vector; + +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; + +/** + * 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. + *

+ * 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. + *

+ * Java 3D allows applications to specify multiple simultaneously active + * View objects, each controlling its own set of canvases. + *

+ * 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. + *

+ * Policies

+ * + * The View object defines the following policies:

+ *

    + *
  • 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:
  • + *

      + *
    • 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.
    • + *

    • 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).
    • + *

    + *
  • 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:
  • + *

      + *
    • PARALLEL_PROJECTION - specifies that a parallel projection + * transform is computed.
    • + *

    • PERSPECTIVE_PROJECTION - specifies that a perspective projection + * transform is computed. This is the default policy.
    • + *

    + *
  • Screen scale policy - specifies where the screen scale comes from. + * There are two screen scale policies:
  • + *

      + *
    • SCALE_SCREEN_SIZE - specifies that the scale is derived from the + * physical screen according to the following formula (this is the + * default mode):
    • + *
        + * screenScale = physicalScreenWidth / 2.0

        + *

      + *
    • SCALE_EXPLICIT - pecifies that the scale is taken directly from + * the user-provided screenScale attribute (see the + * setScreenScale method description).
    • + *

    + *
  • 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:
  • + *

      + *
    • 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.
    • + *

    • 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.
    • + *

    + *
  • 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:
  • + *

      + *
    • 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.)
    • + *

    • PHYSICAL_WORLD - implies that the image seen in the window + * remains the same no matter where the user positions the window + * on the screen.
    • + *

    + *
  • 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:
  • + *

      + *
    • 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.
    • + *

    • 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.
    • + *

    • 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.
    • + *

    • 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.
    • + *

    + *
  • 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:
  • + *

      + *
    • VIRTUAL_EYE - specifies that the associated distance is from + * the eye and in units of virtual distance.
    • + *

    • PHYSICAL_EYE - specifies that the associated distance is from + * the eye and in units of physical distance (in meters). + * This is the default policy for both front and back clipping.
    • + *

    • VIRTUAL_SCREEN - specifies that the associated distance is + * from the screen and in units of virtual distance.
    • + *

    • PHYSICAL_SCREEN - specifies that the associated distance is + * from the screen and in units of physical distance (in meters). + *
    • + *

    + *
  • Visibility policy - specifies how visible and invisible objects + * are drawn. There are three visibility policies:
  • + *

      + *
    • VISIBILITY_DRAW_VISIBLE - only visible objects are drawn + * (this is the default).
    • + *

    • VISIBILITY_DRAW_INVISIBLE - only invisible objects are drawn.
    • + *

    • VISIBILITY_DRAW_ALL - both visible and invisible + * objects are drawn.
    • + *

    + *
  • 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:
  • + *

      + *
    • 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.
    • + *

    • 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.
    • + *

    + *
+ * Projection and Clip Parameters

+ * The projection and clip parameters determine the view model's + * field of view and the front and back clipping distances.

+ *

    + *
  • 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.
  • + *

  • 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.
  • + *

  • 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.
  • + * There are several considerations to take into account when + * choosing values for the front and back clip distances.

    + *

      + *
    • The front clip distance must be greater than 0.0 in physical + * eye coordinates.
    • + *

    • 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.
    • + *

    • 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.
    • + *

    • 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.
    • + *

    + * Violating any of the above rules will result in undefined behavior. + * In many cases, no picture will be drawn.

    + *

+ * Frame Start Time, Duration, and Number

+ * + * There are five methods used to get information about system + * execution and performance:

+ *

    + * getCurrentFrameStartTime returns the time at which + * the most recent rendering frame started.

    + * getLastFrameDuration returns the duration, in milliseconds, of + * the most recently completed rendering frame.

    + * getFrameNumber returns the frame number for this view.

    + * getMaxFrameStartTimes retrieves the implementation-dependent + * maximum number of frames whose start times will be recorded by + * the system.

    + * getFrameStartTimes copies the last k frame start time values + * into the user-specified array.

    + *

+ * View Traversal and Behavior Scheduling

+ * The following methods control the traversal, the rendering, and + * the execution of the behavior scheduler for this view:

+ *

    + * startBehaviorScheduler starts the behavior scheduler + * running after it has been stopped.

    + * stopBehaviorScheduler stops the behavior scheduler after all + * currently-scheduled behaviors are executed.

    + * isBehaviorSchedulerRunning retrieves a flag that indicates + * whether the behavior scheduler is currently running.

    + * startView starts traversing this view and starts the renderers + * associated with all canvases attached to this view.

    + * stopView stops traversing this view after the current state of + * the scene graph is reflected on all canvases attached to this + * view.

    + * isViewRunning returns a flag indicating whether the traverser + * is currently running on this view.

    + *

+ * 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.

+ * + * Scene Antialiasing

+ * + * 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.

+ *

    + * setSceneAntialiasingEnable sets the scene antialiasing flag.

    + * getSceneAntialiasingEnable returns the scene antialiasing + * flag.

    + *

+ * 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. + *

+ * Note: Scene antialiasing is ignored in pure immediate mode, + * but is supported in mixed-immediate mode. + *

+ * Depth Buffer

+ * + * 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.

+ *

    + * setDepthBufferFreezeTransparent enables depth buffer freezing.

    + * getDepthBufferFreezeTransparent retrieves the depth buffer + * flag.

    + *

+ * 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.

+ * + * Sensors

+ * + * The following methods retrieve the sensor's location in the + * virtual world:

+ *

    + * getSensorToVworld 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.

    + * + * getSensorHotSpotInVworld retrieves the specified sensor's + * last hotspot location in virtual world coordinates.

    + *

+ * + * Compatibility Mode

+ * + * 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.

+ * 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.

+ *

    + * setCompatibilityModeEnable turns compatibility mode on or off. + * Compatibility mode is disabled by default.

    + * getCompatabilityModeEnable returns the compatibility mode + * enable flag.

    + *

+ * 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.

+ * + * Setting the Viewing Transform

+ * + * The View object provides the following compatibility-mode + * methods that operate on the viewing transform.

+ *

    + * setVpcToEc a compatibility mode method that + * specifies the ViewPlatform + * coordinates (VPC) to eye coordinates viewing transform.

    + * getVpcToEc returns the VPC.

    + *

+ * Setting the Projection Transform + *

+ * The View object provides the following compatibility-mode + * methods that operate on the projection transform:

+ *

    + * The setLeftProjection and setRightProjection + * methods specify + * a viewing frustum for the left and right eye that transforms + * points in eye coordinates to clipping coordinates.

    + * + * The getLeftProjection and getRightProjection + * methods return + * the viewing frustum for the left and right eye.

    + *

+ * + *

+ * Additional Information + *

+ * For more information, see the + * Introduction to the Java 3D API and + * View Model + * documents. + * + * @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: + *

    + * physical_screen_width / 2.0 + *
+ * 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. + * This is the default policy for both front and back clipping. + * @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. + *

+ * 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; + + // 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; + + // 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 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: + *

    + * view policy : SCREEN_VIEW
    + * projection policy : PERSPECTIVE_PROJECTION
    + * screen scale policy : SCALE_SCREEN_SIZE
    + * window resize policy : PHYSICAL_WORLD
    + * window movement policy : PHYSICAL_WORLD
    + * window eyepoint policy : RELATIVE_TO_FIELD_OF_VIEW
    + * monoscopic view policy : CYCLOPEAN_EYE_VIEW
    + * front clip policy : PHYSICAL_EYE
    + * back clip policy : PHYSICAL_EYE
    + * visibility policy : VISIBILITY_DRAW_VISIBLE
    + * transparency sorting policy : TRANSPARENCY_SORT_NONE
    + * coexistenceCentering flag : true
    + * compatibility mode : false
    + * left projection : identity
    + * right projection : identity
    + * vpc to ec transform : identity
    + * physical body : null
    + * physical environment : null
    + * screen scale : 1.0
    + * field of view : PI/4
    + * left manual eye in coexistence : (-0.033, 0.0, 0.4572)
    + * right manual eye in coexistence : (0.033, 0.0, 0.4572)
    + * front clip distance : 0.1
    + * back clip distance : 10.0
    + * tracking enable : false
    + * user head to vworld enable : false
    + * list of Canvas3D objects : empty
    + * depth buffer freeze transparent : true
    + * scene antialiasing : false
    + * local eye lighting : false
    + * view platform : null
    + * behavior scheduler running : true
    + * view running : true
    + * minimum frame cycle time : 0
    + *
+ */ + 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. + *
    + *
  • 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). + *
  • + *
  • HMD_VIEW specifies that Java 3D should compute a new viewpoint + * using the sequence of transforms appropriate to head mounted + * display environments. + *
  • + *
+ * 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 = 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: + *
    + *
  • 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). + *
  • + *
  • 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. + *
  • + *
  • 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). + *
  • + *
  • 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). + *
  • + *
+ * 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 + * Canvas3D.setMonoscopicViewPolicy + */ + 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 + * Canvas3D.getMonoscopicViewPolicy + */ + 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. + * + *

+ * 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 not + * 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 not 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 not 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. + *

+ * There are several considerations that need to be taken into + * account when choosing values for the front and back clip + * distances. + *

    + *
  • The front clip distance must be greater than + * 0.0 in physical eye coordinates.
  • + *
  • 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.
  • + *
  • 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. + *
  • 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.
  • + *
+ * 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. + *

+ * 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 + * setFrontClipDistance. + *

+ * 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 = 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: + *

    + *
  • VIRTUAL_EYE, to specify that the associated distance is + * from the eye and in units of virtual distance + *
  • + *
  • PHYSICAL_EYE, to specify that the associated distance is + * from the eye and in units of physical distance (meters) + *
  • + *
  • VIRTUAL_SCREEN, to specify that the associated distance is + * from the screen and in units of virtual distance + *
  • + *
  • PHYSICAL_SCREEN, to specify that the associated distance is + * from the screen and in units of physical distance (meters) + *
  • + *
+ * 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: + *
    + *
  • VIRTUAL_EYE, to specify that the associated distance is + * from the eye and in units of virtual distance + *
  • + *
  • PHYSICAL_EYE, to specify that the associated distance is + * from the eye and in units of physical distance (meters) + *
  • + *
  • VIRTUAL_SCREEN, to specify that the associated distance is + * from the screen and in units of virtual distance + *
  • + *
  • PHYSICAL_SCREEN, to specify that the associated distance is + * from the screen and in units of physical distance (meters) + *
  • + *
+ * 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: + *
    + *
  • VISIBILITY_DRAW_VISIBLE, to specify that only visible objects + * are drawn. + *
  • + *
  • VISIBILITY_DRAW_INVISIBLE, to specify that only invisible objects + * are drawn. + *
  • + *
  • VISIBILITY_DRAW_ALL, to specify that both visible and + * invisible objects are drawn. + *
  • + *
+ * 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 = new J3dMessage(); + 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: + * + *
    + *
  • 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.
  • + * + *
  • 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.
  • + *
+ * + * 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 = new J3dMessage(); + 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 = 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) { + + 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) { + + 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 = 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 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 = 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() { + LinkedList tmpCanvases = new LinkedList(); + + synchronized(canvasList) { + int numCanvases = canvases.size(); + + // Remove in reverse order to ensure valid indices + for (int index = numCanvases - 1; index >= 0; index--) { + Canvas3D cv = canvases.elementAt(index); + + // Record list of canvases to be deleted; + tmpCanvases.add(cv); + + canvases.removeElementAt(index); + removeFromCanvasList(cv); + canvasesDirty = true; + } + } + + // ISSUE 83: postRequest must *not* be called while holding + // canvasList lock. Holding the lock can cause a deadlock. + + Iterator iterator = tmpCanvases.iterator(); + while (iterator.hasNext()) { + Canvas3D cv = iterator.next(); + + // 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 (screenList.get(i) == c.screen) { + // This is the right screen slot + 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 (screenList.get(i) == c.screen) { + // This is the right screen slot + ArrayList clist = canvasList.get(i); + clist.remove(clist.indexOf(c)); + + if (clist.size() == 0) { + canvasList.remove(i); + screenList.remove(i); + canvasesDirty = true; + } + return; + } + } + } + + // Locks are not acquired before this is called. + void computeCanvasesCached() { + + synchronized (canvasList) { + ArrayList cv; + int len = canvases.size(); + + Canvas3D newCachedCanvases[] = new Canvas3D[len]; + for (int i=0; i < len; i++) { + newCachedCanvases[i] = canvases.get(i); + } + // Do this in one instruction so there is no need to + // synchronized getCanvases() + + cachedCanvases = newCachedCanvases; + len = 0; + longestScreenList = 0; + cachedCanvasList = new Canvas3D[canvasList.size()][0]; + for (int i=0; i < cachedCanvasList.length; i++) { + cv = canvasList.get(i); + len = cv.size(); + cachedCanvasList[i] = new Canvas3D[len]; + for (int j=0; j < len; j++) { + cachedCanvasList[i][j] = cv.get(j); + } + + if (len > longestScreenList) { + longestScreenList = len; + } + } + len = screenList.size(); + Screen3D newCachedScreens[] = new Screen3D[len]; + + for (int i=0; i < len; i++) { + newCachedScreens[i] = 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; + } + + // assume getCanvasList is called before + Screen3D[] getScreens() { + return cachedScreens; + } + + Canvas3D getFirstCanvas() { + synchronized (canvasList) { + if (canvases.size() > 0) { + return 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 k 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 NUMBER_FRAME_START_TIMES) { + for (; iminimumTime < 0 + * + * @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 - + (J3dClock.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. + *

+ * 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. + *

+ * NOTE: This is a heavy-weight method + * intended for verification and image capture (recording); it + * is not 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 (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. + *

+ * NOTE: This is a heavy-weight method + * intended for verification and image capture (recording); it + * is not 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 = 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 startView() followed by + * stopview(), 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() { + + 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 = 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 = 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(); + } + + J3dMessage vpMessage = new J3dMessage(); + 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 + // XXXX:cleanup in RenderBin after View detach + // universe.addViewIdToFreeList(viewId); + + // using new property -Dj3d.forceReleaseView to disable bug fix 4267395 + // this bug fix can produce memory leaks in *some* applications which creates + // and destroy Canvas3D from time to time. This just add the view in the + // FreeList earlier. + if (VirtualUniverse.mc.forceReleaseView) { + universe.addViewIdToFreeList(viewId); + } + + + J3dMessage vpMessage = new J3dMessage(); + 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++) { + canvases.get(i).reset(); + } + + // reset the renderBinReady flag + renderBinReady = false; + + soundScheduler.cleanup(); + soundScheduler = null; + + viewCache = new ViewCache(this); + getCanvasList(true); + cleanupViewId(); + renderBin.cleanup(); + renderBin = null; + universe = null; + } +} diff --git a/src/javax/media/j3d/ViewCache.java b/src/javax/media/j3d/ViewCache.java new file mode 100644 index 0000000..78ac9f0 --- /dev/null +++ b/src/javax/media/j3d/ViewCache.java @@ -0,0 +1,356 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import javax.vecmath.Point3d; + +/** + * 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.err.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.err.println("trackerBaseToHeadTracker: "); + //System.err.println(trackerBaseToHeadTracker); + } + else { + trackerBaseToHeadTracker.setIdentity(); + } + + // XXXX: 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.err.println("Constructed a ViewCache"); + } + +} diff --git a/src/javax/media/j3d/ViewPlatform.java b/src/javax/media/j3d/ViewPlatform.java new file mode 100644 index 0000000..d99447b --- /dev/null +++ b/src/javax/media/j3d/ViewPlatform.java @@ -0,0 +1,289 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + *

+ * The View Attach Policy + *

+ * 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: + *

+ *

    + *
  • 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.
  • + *

    + *

  • 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.
  • + *

    + *

  • 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.
+ *

+ * Activation Radius + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ *

    + * 62 * 0.35 / 2 = 10.85 + *
+ *

+ * + * @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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_POLICY_READ + }; + + /** + * Constructs a ViewPlatform object with default parameters. + * The default values are as follows: + *

    + * view attach policy : View.NOMINAL_HEAD
    + * activation radius : 62
    + *
+ */ + public ViewPlatform() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + /** + * Creates the retained mode ViewPlatformRetained object that this + * ViewPlatform component object will point to. + */ + @Override + 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 cloneTree to duplicate the current node. + * cloneNode should be overridden by any user subclassed + * objects. All subclasses must have their cloneNode + * method consist of the following lines: + *

+     *     public Node cloneNode(boolean forceDuplicate) {
+     *         UserSubClass usc = new UserSubClass();
+     *         usc.duplicateNode(this, forceDuplicate);
+     *         return usc;
+     *     }
+     * 
+ * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + ViewPlatform v = new ViewPlatform(); + v.duplicateNode(this, forceDuplicate); + return v; + } + + + /** + * Copies all ViewPlatform information from originalNode into + * the current node. This method is called from the + * duplicateNode 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 + * duplicateNode method. This is designate by setting the + * duplicateRetained flag to true. + * Without this flag a duplicateNode 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 true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * @param duplicateRetained set to true 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 + */ + + @Override + 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/javax/media/j3d/ViewPlatformRetained.java b/src/javax/media/j3d/ViewPlatformRetained.java new file mode 100644 index 0000000..4d9f069 --- /dev/null +++ b/src/javax/media/j3d/ViewPlatformRetained.java @@ -0,0 +1,418 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; + +import javax.vecmath.Point3d; + +/** + * 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((Bounds)null); + 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 = new J3dMessage(); + 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. + */ +void setView(View v) { + synchronized (viewList) { + if (!viewList.contains(v)) { + viewList.add(v); + viewListDirty = true; + } + } +} + +void removeView(View v) { + synchronized (viewList) { + if (viewList.remove(v)) + viewListDirty = true; + } +} + + Transform3D getVworldToVpc() { + if (vworldToVpc == null) + vworldToVpc = new Transform3D(); + vworldToVpc.set(getCurrentLocalToVworld(null)); + vworldToVpc.invert(); + return vworldToVpc; + } + + Transform3D getVpcToVworld() { + vpcToVworld .set(getCurrentLocalToVworld(null)); + return vpcToVworld; + } + + + void evaluateViewPlatformTransform() { + // 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. + */ + @Override + 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.err.println("setLive: vworldToVpc = "); + System.err.println(this.vworldToVpc); + System.err.println("setLive: vpcToVworld = "); + System.err.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 = 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. + */ + @Override + 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 = 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(); + for (int i = 0; i < v.length; i++) { + if (v[i].active) { + return true; + } + } + + return false; +} + + void processSwitchChanged() { + reEvaluateView(); + } + + + @Override + 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/javax/media/j3d/ViewSpecificGroup.java b/src/javax/media/j3d/ViewSpecificGroup.java new file mode 100644 index 0000000..b8913a9 --- /dev/null +++ b/src/javax/media/j3d/ViewSpecificGroup.java @@ -0,0 +1,360 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * All nodes except ViewPlatform may appear as a descendant of + * ViewSpecificGroup, including another ViewSpecificGroup. If a + * ViewSpecificGroup is a descendant of a ViewSpecificGroup, the + * effect is to intersect the view sets of the ViewSpecificGroup nodes + * in the hierarchy; each ViewSpecificGroup encountered when + * traversing the scene graph further restricts the set of views on + * which its descendants are rendered. More formally, descendant + * nodes of ViewSpecificGroups are rendered in (or apply to) only + * those views that are contained in the set of views of every + * ViewSpecificGroup in the scene graph path from the Locale to the + * Node. + * + *

+ * 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; + + // Array for setting default read capabilities + private static final int[] readCapabilities = { + ALLOW_VIEW_READ + }; + + /** + * Constructs and initializes a new ViewSpecificGroup node object. + */ + public ViewSpecificGroup() { + // set default read capabilities + setDefaultReadCapabilities(readCapabilities); + } + + + /** + * Creates the retained mode ViewSpecificGroupRetained object that this + * ViewSpecificGroup component object will point to. + */ + @Override + 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() && !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 cloneTree to duplicate the current node. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree variable determines whether + * NodeComponent data is duplicated or copied. + * + * @see Node#cloneTree + * @see Node#cloneNode + * @see Node#duplicateNode + * @see NodeComponent#setDuplicateOnCloneTree + */ + @Override + public Node cloneNode(boolean forceDuplicate) { + ViewSpecificGroup vsg = new ViewSpecificGroup(); + vsg.duplicateNode(this, forceDuplicate); + return vsg; + } + + + /** + * Copies all ViewSpecificGroup information from + * originalNode into + * the current node. This method is called from the + * cloneNode method which is, in turn, called by the + * cloneTree method.

+ * + * @param originalNode the original node to duplicate. + * @param forceDuplicate when set to true, causes the + * duplicateOnCloneTree flag to be ignored. When + * false, the value of each node's + * duplicateOnCloneTree 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 + */ + @Override + void duplicateAttributes(Node originalNode, boolean forceDuplicate) { + + // XXXX: implement this? + super.duplicateAttributes(originalNode, forceDuplicate); + + ViewSpecificGroupRetained attr = (ViewSpecificGroupRetained) originalNode.retained; + ViewSpecificGroupRetained rt = (ViewSpecificGroupRetained) retained; + + for (Enumeration e = attr.getAllViews(); e.hasMoreElements(); ) { + rt.addView(e.nextElement()); + } + } + +} diff --git a/src/javax/media/j3d/ViewSpecificGroupRetained.java b/src/javax/media/j3d/ViewSpecificGroupRetained.java new file mode 100644 index 0000000..69fde27 --- /dev/null +++ b/src/javax/media/j3d/ViewSpecificGroupRetained.java @@ -0,0 +1,759 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +/** + * 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) { + for (int k = 0; k < localToVworldKeys.length; k++) { + ArrayList parentList = 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.err.println("Shared:n = "+n+" addLeafList = "+addLeafList.get(n)); + } + */ + objAry[3] = super.processViewSpecificInfo(ADD_VIEW, + key, view, + addVsgList, addKeyList, addLeafList); + J3dMessage message = new J3dMessage(); + 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 = 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.err.println("n = "+n+" addLeafList = "+addLeafList.get(n)); + } + */ + + J3dMessage message = new J3dMessage(); + 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) { + View oldView = 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) { + for (int k = 0; k < localToVworldKeys.length; k++) { + ArrayList parentList = 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, 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, key, + view, addVsgList, addKeyList, addLeafList); + } + J3dMessage message = new J3dMessage(); + 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 = 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, 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, tempKey, + view, addVsgList, addKeyList, addLeafList); + } + J3dMessage message = new J3dMessage(); + 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); + } + + + } + + } + + @Override + 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 = parentLists.get(hkIndex); + parentList.add(v); + } + else if (mode == REMOVE_VIEW) { + ArrayList parentList = parentLists.get(hkIndex); + parentList.remove(v); + } + if(apiViewList.contains(v)) { + // System.err.println("processViewSpecificInfo, this = "+this+" key = "+key); + vsgList.add(this); + if (keyList.length< vsgList.size()) { + // System.err.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 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) { + for (int k = 0; k < localToVworldKeys.length; k++) { + ArrayList parentList = 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.err.println("Shared:n = "+n+" addLeafList = "+addLeafList.get(n)); + } + */ + objAry[3] = super.processViewSpecificInfo(ADD_VIEW, + key, view, + addVsgList, addKeyList, addLeafList); + J3dMessage message = new J3dMessage(); + 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 = 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.err.println("n = "+n+" addLeafList = "+addLeafList.get(n)); + } + */ + + J3dMessage message = new J3dMessage(); + 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 = apiViewList.remove(index); + if (source.isLive() && v != null) { + // Gather all affected leaf nodes and send a message to + // RenderingEnv and RenderBin + if (inSharedGroup) { + for (int k = 0; k < localToVworldKeys.length; k++) { + ArrayList parentList = 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, + key,v, + removeVsgList, removeKeyList, removeLeafList); + + + J3dMessage message = new J3dMessage(); + 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 = 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, + tempKey, v, + removeVsgList, removeKeyList, removeLeafList); + + /* + for (int n = 0; n < removeKeyList.size(); n++) { + System.err.println("n = "+n+" keyValue = "+removeKeyList.get(n)); + } + */ + J3dMessage message = new J3dMessage(); + 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(apiViewList.size()); + 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); + } + } + + @Override + void compile(CompileState compState) { + super.compile(compState); + + // don't remove this group node + mergeFlag = SceneGraphObjectRetained.DONT_MERGE; + + // XXXX: complete this + } + + @Override + 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; + + } + + @Override + 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>(); + } + // XXXX: 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; + } + + + @Override + 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.err.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.err.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 = cachedViewList.get(index); + + /* + System.err.println("updateCachedInformation v = "+this+" index = "+index+" list = "+list+" cachedViewList.size() = "+cachedViewList.size()); + for (int k = 0; k < cachedViewList.size(); k++) { + System.err.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.err.println("After updateCachedInformation v = "+this+" index = "+index+" list = "+list+" cachedViewList.size() = "+cachedViewList.size()); + for (int k = 0; k < cachedViewList.size(); k++) { + System.err.println("v = "+this+" k = "+k+" v.cachedViewList.get(k) = "+cachedViewList.get(k)); + } + */ + + } + + @Override + 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.err.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.err.println("====> setNodeData: Allocating Shared"); + } + + for(j=0; j= 0) { + setAuxData(s, j, hkIndex); + + } else { + MasterControl.getCoreLogger().severe("Can't Find matching hashKey in setNodeData."); + } + } + } + // Make the VSG's viewLists as the relavant one for its children + s.viewLists = viewLists; + + } + + @Override + void setAuxData(SetLiveState s, int index, int hkIndex) { + ArrayList vl = new ArrayList(); + ArrayList parentList = null; + int size = apiViewList.size(); + if (s.viewLists != null) { + // System.err.println("=====> VSG: = "+this+" hkIndex = "+hkIndex+" s.viewLists = "+s.viewLists); + parentList = s.viewLists.get(hkIndex); + if (parentList != null) { + for (int i = 0; i < size; i++) { + View v = apiViewList.get(i); + // Get the intersection of the parentlist and this vsg's api list + for (int j = 0; j < parentList.size(); j++) { + if (v == parentList.get(j)) { + vl.add(v); + break; + } + } + } + } + else { + // Only include the non null ones in the apiViewList + for (int i = 0; i < size; i++) { + View v = apiViewList.get(i); + if (v != null) { + vl.add(v); + } + } + } + } + else { + // Only include the non null ones in the apiViewList + for (int i = 0; i < size; i++) { + View v = apiViewList.get(i); + if (v != null) { + vl.add(v); + } + } + } + if (parentList != null) { + parentLists.add(hkIndex, new ArrayList(parentList)); + } + 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/javax/media/j3d/VirtualUniverse.java b/src/javax/media/j3d/VirtualUniverse.java new file mode 100644 index 0000000..624bccb --- /dev/null +++ b/src/javax/media/j3d/VirtualUniverse.java @@ -0,0 +1,1220 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * 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. + *

+ * A VirtualUniverse object defines methods to enumerate its Locale + * objects and to remove them from the virtual universe. + * + *

+ * For more information, see the + * Introduction to the Java 3D API and + * Scene Graph Superstructure + * documents. + * + * @see Locale + */ + +public class VirtualUniverse extends Object { + // NOTE TO DEVELOPERS: + // + // Developers who modify Java 3D in any way should 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, a changed flag and a cached array +private ArrayList viewPlatforms = new ArrayList(); +private boolean vpChanged = false; +private ViewPlatformRetained[] viewPlatformList = new ViewPlatformRetained[0]; + + // 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; + + // 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; + boolean enableMouseWheel = false; + + // Keep track of how many active View use this universe + int activeViewCount = 0; + + // Root ThreadGroup for creating Java 3D threads + static ThreadGroup rootThreadGroup; + + // 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(); + + // Set of scene graph structure change listeners + private HashSet structureChangeListenerSet = null; + + // Set of shader error listeners + private HashSet shaderErrorListenerSet = null; + private ShaderErrorListener defaultShaderErrorListener = + ShaderProgram.getDefaultErrorListener(); + + // Set of rendering error listeners + private static HashSet renderingErrorListenerSet = null; + private static RenderingErrorListener defaultRenderingErrorListener = + Renderer.getDefaultErrorListener(); + + /** + * 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); + + } + + /** + * 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 { + boolean isLoggableConfig = MasterControl.isCoreLoggable(Level.CONFIG); + Logger logger = MasterControl.getCoreLogger(); + + // Print out version information unless this is a + // non-debuggable, release (fcs) build + if (isLoggableConfig || J3dDebug.devPhase || VersionInfo.isDebug) { + StringBuffer strBuf = new StringBuffer("3D "); + if (J3dDebug.devPhase) { + strBuf.append("[dev] "); + } + strBuf.append(VersionInfo.getVersion()); + String str = strBuf.toString(); + if (isLoggableConfig) { + logger.config(str); + } else { + System.err.println(str); + System.err.println(); + } + } + + // Print out debugging information for debug builds + if (isLoggableConfig || VersionInfo.isDebug) { + StringBuffer strBuf = new StringBuffer(); + strBuf.append("Initializing 3D runtime system:\n"). + append(" version = "). + append(VersionInfo.getVersion()). + append("\n"). + append(" vendor = "). + append(VersionInfo.getVendor()). + append("\n"). + append(" specification.version = "). + append(VersionInfo.getSpecificationVersion()). + append("\n"). + append(" specification.vendor = "). + append(VersionInfo.getSpecificationVendor()); + String str = strBuf.toString(); + if (isLoggableConfig) { + logger.config(str); + } else { + System.err.println(str); + System.err.println(); + } + } + + // Java 3D cannot run in headless mode, so we will throw a + // HeadlessException if isHeadless() is true. This avoids a + // cryptic error message from MasterControl.loadLibraries(). + if (java.awt.GraphicsEnvironment.isHeadless()) { + throw new java.awt.HeadlessException(); + } + + // Load the native libraries and create the static + // MasterControl object + MasterControl.loadLibraries(); + mc = new MasterControl(); + + // Print out debugging information for debug builds + if (isLoggableConfig || VersionInfo.isDebug) { + StringBuffer strBuf = new StringBuffer(); + strBuf.append("3D system initialized\n"). + append(" rendering pipeline = "). + append(Pipeline.getPipeline().getPipelineName()); + String str = strBuf.toString(); + if (isLoggableConfig) { + logger.config(str); + } else { + System.err.println(str); + System.err.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--) { + 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 = 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. + * + *

+ * The set of global Java 3D properties always includes values for + * the following keys: + * + *

+ *

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Key (String)Value Type
    j3d.versionString
    j3d.vendorString
    j3d.specification.versionString
    j3d.specification.vendorString
    j3d.pipelineString
    j3d.rendererString
    + *
+ * + *

+ * The descriptions of the values returned for each key are as follows: + * + *

+ *

    + * + *
  • + * j3d.version + *
      + * 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): + *
        + * x.y.z
        + * x.y.z_p
        + * x.y.z-ssss
        + *
      + * where: + *
        + * x is the major version number
        + * y is the minor version number
        + * z is the sub-minor version number
        + * p is the patch revision number
        + * ssss is a string, identifying a non-release build + * (e.g., beta1, build47, rc1, etc.). It may only + * contain letters, numbers, periods, dashes, or + * underscores. + *
      + *
    + *
  • + *

    + * + *

  • + * j3d.vendor + *
      + * String that specifies the Java 3D implementation vendor. + *
    + *
  • + *

    + * + *

  • + * j3d.specification.version + *
      + * A String that defines the Java 3D specification version. + * This string must be of the following form: + *
        + * x.y + *
      + * where: + *
        + * x is the major version number
        + * y is the minor version number
        + *
      + * No other characters are allowed in the specification version string. + *
    + *
  • + *

    + * + *

  • + * j3d.specification.vendor + *
      + * String that specifies the Java 3D specification vendor. + *
    + *
  • + *

    + * + *

  • + * j3d.pipeline + *
      + * String that specifies the Java 3D rendering pipeline. This could + * be one of: "NATIVE_OGL", "NATIVE_D3D", or "JOGL". Others could be + * added in the future. + *
    + *
  • + *

    + * + *

  • + * j3d.renderer + *
      + * String that specifies the underlying rendering library. This could + * be one of: "OpenGL" or "DirectX". Others could be added in the future. + *
    + *
  • + *

    + * + *

+ * + * @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(Pipeline.getPipeline().getRendererName()); + + keys.add("j3d.pipeline"); + values.add(Pipeline.getPipeline().getPipelineName()); + + // Now Create read-only properties object + properties = new J3dQueryProps(keys, values); + } + return properties; + } + + + /** + * This returns the next available nodeId as a string. + */ + // XXXX: 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 { + // Issue 496: Remove last object using index to avoid performance + // hit of a needless linear search. + int idx = nodeIdFreeList.size() - 1; + str = nodeIdFreeList.remove(idx); + } + 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 = 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) { + synchronized (viewPlatforms) { + vpChanged = true; + viewPlatforms.add(vp); + } +} + +void removeViewPlatform(ViewPlatformRetained vp) { + synchronized (viewPlatforms) { + if (viewPlatforms.remove(vp)) + vpChanged = true; + } +} + +ViewPlatformRetained[] getViewPlatformList() { + synchronized (viewPlatforms) { + if (vpChanged) { + viewPlatformList = viewPlatforms.toArray(new ViewPlatformRetained[viewPlatforms.size()]); + vpChanged = false; + } + + return viewPlatformList; + } +} + + void checkForEnableEvents() { + if (enableFocus) { + enableFocusEvents(); + } + if (enableKey) { + enableKeyEvents(); + } + if (enableMouse) { + enableMouseEvents(); + } + if (enableMouseMotion) { + enableMouseMotionEvents(); + } + if (enableMouseWheel) { + enableMouseWheelEvents(); + } + + } + +void disableFocusEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableFocus = false; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.disableFocusEvents(); + } + } + } +} + +void enableFocusEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableFocus = true; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.enableFocusEvents(); + } + } + } +} + +void disableKeyEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableKey = false; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.disableKeyEvents(); + } + } + } +} + +void enableKeyEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableKey = true; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.enableKeyEvents(); + } + } + } +} + +void disableMouseEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableMouse = false; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.disableMouseEvents(); + } + } + } +} + +void enableMouseEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableMouse = true; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.enableMouseEvents(); + } + } + } +} + +void disableMouseMotionEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableMouseMotion = false; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.disableMouseMotionEvents(); + } + } + } +} + +void enableMouseMotionEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableMouseMotion = true; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.enableMouseMotionEvents(); + } + } + } +} + +void disableMouseWheelEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableMouseWheel = false; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.disableMouseWheelEvents(); + } + } + } +} + +void enableMouseWheelEvents() { + ViewPlatformRetained[] vps = getViewPlatformList(); + enableMouseWheel = true; + + for (int i = 0; i < vps.length; i++) { + View[] views = vps[i].getViewList(); + for (int j = views.length - 1; j >= 0; j--) { + Enumeration cvs = views[j].getAllCanvas3Ds(); + while (cvs.hasMoreElements()) { + Canvas3D cv = cvs.nextElement(); + // offscreen canvas does not have event catcher + if (cv.eventCatcher != null) + cv.eventCatcher.enableMouseWheelEvents(); + } + } + } +} + + /** + * 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 = 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; + } + } + + /** + * Adds the specified GraphStructureChangeListener to the set of listeners + * that will be notified when the graph structure is changed on a live + * scene graph. If the specifed listener is null no action is taken and no + * exception is thrown. + * + * @param listener the listener to add to the set. + * + * @since Java 3D 1.4 + */ + public void addGraphStructureChangeListener(GraphStructureChangeListener listener) { + if (listener == null) { + return; + } + + if (structureChangeListenerSet == null) { + structureChangeListenerSet = new HashSet(); + } + + synchronized(structureChangeListenerSet) { + structureChangeListenerSet.add(listener); + } + } + + /** + * Removes the specified GraphStructureChangeListener from the set of listeners. This + * method performs no function, nor does it throw an exception if the specified listener + * is not currently in the set or is null. + * + * @param listener the listener to remove from the set. + * + * @since Java 3D 1.4 + */ + public void removeGraphStructureChangeListener(GraphStructureChangeListener listener) { + if (structureChangeListenerSet == null) { + return; + } + + synchronized(structureChangeListenerSet) { + structureChangeListenerSet.remove(listener); + } + } + + /** + * Processes all live BranchGroup add and removes and notifies + * any registered listeners. Used for add and remove + */ + void notifyStructureChangeListeners(boolean add, Object parent, BranchGroup child) { + if (structureChangeListenerSet == null) { + return; + } + + synchronized(structureChangeListenerSet) { + Iterator it = structureChangeListenerSet.iterator(); + while(it.hasNext()) { + GraphStructureChangeListener listener = it.next(); + try { + if (add) { + listener.branchGroupAdded(parent, child); + } else { + listener.branchGroupRemoved(parent, child); + } + } + catch (RuntimeException e) { + System.err.println("Exception occurred in GraphStructureChangeListener:"); + e.printStackTrace(); + } + catch (Error e) { + // Issue 264 - catch Error + System.err.println("Error occurred in GraphStructureChangeListener:"); + e.printStackTrace(); + } + } + } + } + + /** + * Processes all live BranchGroup moves and notifies + * any registered listeners. Used for moveTo + */ + void notifyStructureChangeListeners(Object oldParent, Object newParent, BranchGroup child) { + if (structureChangeListenerSet == null) { + return; + } + + synchronized(structureChangeListenerSet) { + Iterator it = structureChangeListenerSet.iterator(); + while(it.hasNext()) { + GraphStructureChangeListener listener = it.next(); + try { + listener.branchGroupMoved(oldParent, newParent, child); + } + catch (RuntimeException e) { + System.err.println("Exception occurred in GraphStructureChangeListener:"); + e.printStackTrace(); + } + catch (Error e) { + // Issue 264 - catch Error + System.err.println("Error occurred in GraphStructureChangeListener:"); + e.printStackTrace(); + } + } + } + } + + + /** + * Adds the specified ShaderErrorListener to the set of listeners + * that will be notified when a programmable shader error is + * detected on a live scene graph. If the specifed listener is + * null no action is taken and no exception is thrown. + * If a shader error occurs, the listeners will be called + * asynchronously from a separate notification thread. The Java 3D + * renderer and behavior scheduler will continue to run as if the + * error had not occurred, except that shading will be disabled + * for the objects in error. If applications desire to detach or + * modify the scene graph as a result of the error, they should + * use a behavior post if they want that change to be + * synchronous with the renderer. + * + * @param listener the listener to add to the set. + * + * @since Java 3D 1.4 + */ + public void addShaderErrorListener(ShaderErrorListener listener) { + if (listener == null) { + return; + } + + if (shaderErrorListenerSet == null) { + shaderErrorListenerSet = new HashSet(); + } + + synchronized(shaderErrorListenerSet) { + shaderErrorListenerSet.add(listener); + } + } + + /** + * Removes the specified ShaderErrorListener from the set of + * listeners. This method performs no function, nor does it throw + * an exception if the specified listener is not currently in the + * set or is null. + * + * @param listener the listener to remove from the set. + * + * @since Java 3D 1.4 + */ + public void removeShaderErrorListener(ShaderErrorListener listener) { + if (shaderErrorListenerSet == null) { + return; + } + + synchronized(shaderErrorListenerSet) { + shaderErrorListenerSet.remove(listener); + } + } + + /** + * Notifies all listeners of a shader error. If no listeners exist, a default + * listener is notified. + */ + void notifyShaderErrorListeners(ShaderError error) { + boolean errorReported = false; + + // Notify all error listeners in the set + if (shaderErrorListenerSet != null) { + synchronized(shaderErrorListenerSet) { + Iterator it = shaderErrorListenerSet.iterator(); + while(it.hasNext()) { + ShaderErrorListener listener = it.next(); + try { + listener.errorOccurred(error); + } + catch (RuntimeException e) { + System.err.println("Exception occurred in ShaderErrorListener:"); + e.printStackTrace(); + } + catch (Error e) { + // Issue 264 - catch Error + System.err.println("Error occurred in ShaderErrorListener:"); + e.printStackTrace(); + } + errorReported = true; + } + } + } + + // Notify the default error listener if the set is null or empty + if (!errorReported) { + defaultShaderErrorListener.errorOccurred(error); + } + } + + + // Issue 260 : rendering error listeners. + + /** + * Adds the specified RenderingErrorListener to the set of listeners + * that will be notified when a rendering error is detected. + * If the specifed listener is null no action is taken and no exception + * is thrown. + * If a rendering error occurs, the listeners will be called + * asynchronously from a separate notification thread. If the set + * of listeners is empty, a default listener is notified. The + * default listener prints the error information to System.err and + * then calls System.exit(). + * + * @param listener the listener to add to the set. + * + * @since Java 3D 1.5 + */ + public static void addRenderingErrorListener(RenderingErrorListener listener) { + if (listener == null) { + return; + } + + if (renderingErrorListenerSet == null) { + renderingErrorListenerSet = new HashSet(); + } + + synchronized(renderingErrorListenerSet) { + renderingErrorListenerSet.add(listener); + } + } + + /** + * Removes the specified RenderingErrorListener from the set of + * listeners. This method performs no function, nor does it throw + * an exception if the specified listener is not currently in the + * set or is null. + * + * @param listener the listener to remove from the set. + * + * @since Java 3D 1.5 + */ + public static void removeRenderingErrorListener(RenderingErrorListener listener) { + if (renderingErrorListenerSet == null) { + return; + } + + synchronized(renderingErrorListenerSet) { + renderingErrorListenerSet.remove(listener); + } + } + + /** + * Notifies all listeners of a rendering error. If no listeners exist, + * a default listener is notified. + */ + static void notifyRenderingErrorListeners(RenderingError error) { + boolean errorReported = false; + + // Notify all error listeners in the set + if (renderingErrorListenerSet != null) { + synchronized(renderingErrorListenerSet) { + Iterator it = renderingErrorListenerSet.iterator(); + while(it.hasNext()) { + RenderingErrorListener listener = it.next(); + try { + listener.errorOccurred(error); + } + catch (RuntimeException e) { + System.err.println("Exception occurred in RenderingErrorListener:"); + e.printStackTrace(); + } + catch (Error e) { + // Issue 264 - catch Error + System.err.println("Error occurred in RenderingErrorListener:"); + e.printStackTrace(); + } + errorReported = true; + } + } + } + + // Notify the default error listener if the set is null or empty + if (!errorReported) { + defaultRenderingErrorListener.errorOccurred(error); + } + } + +} diff --git a/src/javax/media/j3d/WakeupAnd.java b/src/javax/media/j3d/WakeupAnd.java new file mode 100644 index 0000000..a00c2fe --- /dev/null +++ b/src/javax/media/j3d/WakeupAnd.java @@ -0,0 +1,133 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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. + *

+ * 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 + */ + @Override + void setConditionMet(int id, Boolean checkSchedulingRegion) { + conditionsMet[id] = true; + + for (int i=0; i + * 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 + */ + @Override + void setConditionMet(int id, Boolean checkSchedulingRegion) { + conditionsMet[id] = true; + + for (int i=0; i + * 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. + * + */ + @Override + 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. + * + */ + @Override + void cleanTree(BehaviorStructure bs){ + conditionMet = false; + removeBehaviorCondition(bs); + }; + + + /** + * This goes through the AndOr tree to reset various criterion. + */ + @Override + 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/javax/media/j3d/WakeupIndexedList.java b/src/javax/media/j3d/WakeupIndexedList.java new file mode 100644 index 0000000..5eb7de1 --- /dev/null +++ b/src/javax/media/j3d/WakeupIndexedList.java @@ -0,0 +1,600 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * Use the following code to iterate through an array. + * + *

+ *  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];
+ *      ....
+ *  }
+ * 
+ * + *

+ * Note: + *

    + * 1) The array return is a copied of internal array.
    + * 2) Don't use arr.length , use list.arraySize();
    + * 3) No need to do casting for individual element as in + * ArrayList.
    + * 4) WakeupIndexedList is thread safe.
    + * 5) Object implement this interface MUST initialize the index + * to -1.
    + *
+ * + *

+ * Limitation: + *

    + * - Same element can't add in two different WakeupIndexedList
    + * - Order of WakeupCondition is not important
    + * - Can't modify the clone() copy.
    + * - Object can't be null
    + *
+ */ + +class WakeupIndexedList implements Cloneable, java.io.Serializable { + + // XXXX: 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 true if this list has no elements; + * false otherwise. + */ + final boolean isEmpty() { + return size == 0; + } + + /** + * Returns true 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 equals method. + * + * @param o an object. + * @return the index of the first occurrence of the argument in this + * list; returns -1 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 ArrayList instance. (The + * elements themselves are not copied.) + * + * @return a clone of this ArrayList instance. + */ + @Override + 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 ArrayList instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an ArrayList 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 (index + * < 0 || index >= size()). + */ + 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 + * (index < 0 || index >= size()). + */ + 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.err.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.err.println(this + " add " + o + " univ " + univIdx); + if (debug) { + int idx = o.listIdx[univIdx][listType]; + if (idx >= 0) { + if (elementData[idx] != o) { + System.err.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 (index + * < 0 || index >= size()). + */ + 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.err.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 (index + * < 0 || index >= size()). + */ + synchronized final boolean remove(WakeupCondition o) { + int univIdx = o.behav.getIdxUsed(univ); + int idx = o.listIdx[univIdx][listType]; + + // System.err.println(this + " remove " + o + " univ " + univIdx); + + if (idx >= 0) { + // Object in the container + if (debug) { + if (o != elementData[idx]) { + System.err.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; + } + + @Override + 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 ArrayList instance to a stream (that + * is, serialize it). + * + * @serialData The length of the array backing the ArrayList + * instance is emitted (int), followed by all of its elements + * (each an Object) 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; iArrayList 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 events = new ArrayList(); + + /** + * 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() { + synchronized (events) { + AWTEvent[] eventArray = events.toArray(new AWTEvent[events.size()]); + events.clear(); + return eventArray; + } +} + + /** + * Sets the AWT event that will cause a behavior wakeup. + * @param event The event causing this wakeup + */ +void addAWTEvent(AWTEvent event) { + synchronized (events) { + events.add(event); + } + this.setTriggered(); +} + + /** + * This is a callback from BehaviorStructure. It is + * used to add wakeupCondition to behavior structure. + */ + @Override + 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. + */ + @Override + void removeBehaviorCondition(BehaviorStructure bs) { + bs.wakeupOnAWTEvent.remove(this); + } + + /** + * Perform task in addBehaviorCondition() that has to be + * set every time the condition met. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) { + if (enableAWTEventTS != bs.awtEventTimestamp) { + 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 if (AwtId == MouseEvent.MOUSE_WHEEL) { + behav.universe.enableMouseWheelEvents(); + } + else if (AwtId == MouseEvent.MOUSE_CLICKED || + AwtId == MouseEvent.MOUSE_ENTERED || + AwtId == MouseEvent.MOUSE_EXITED || + AwtId == MouseEvent.MOUSE_PRESSED || + AwtId == MouseEvent.MOUSE_RELEASED) { + 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(); + } + if ((EventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0) { + behav.universe.enableMouseWheelEvents(); + } + } + enableAWTEventTS = bs.awtEventTimestamp; + } + } +} diff --git a/src/javax/media/j3d/WakeupOnActivation.java b/src/javax/media/j3d/WakeupOnActivation.java new file mode 100644 index 0000000..6b438c3 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnActivation.java @@ -0,0 +1,82 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) {} +} diff --git a/src/javax/media/j3d/WakeupOnBehaviorPost.java b/src/javax/media/j3d/WakeupOnBehaviorPost.java new file mode 100644 index 0000000..4e2d9c5 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnBehaviorPost.java @@ -0,0 +1,130 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + void addBehaviorCondition(BehaviorStructure bs) { + bs.wakeupOnBehaviorPost.add(this); + } + + + /** + * This is a callback from BehaviorStructure. It is + * used to remove wakeupCondition from behavior structure. + */ + @Override + void removeBehaviorCondition(BehaviorStructure bs) { + bs.wakeupOnBehaviorPost.remove(this); + } + + /** + * Perform task in addBehaviorCondition() that has to be + * set every time the condition met. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) {} +} diff --git a/src/javax/media/j3d/WakeupOnCollisionEntry.java b/src/javax/media/j3d/WakeupOnCollisionEntry.java new file mode 100644 index 0000000..27fcd63 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnCollisionEntry.java @@ -0,0 +1,595 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.Vector; + +/** + * 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 arm 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 arm 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 + * arm 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 + * arm 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 arm collision + * detection + */ + public WakeupOnCollisionEntry(Bounds armingBounds) { + this(null, USE_BOUNDS, (Bounds) armingBounds.clone()); + } + + /** + * Constructs a new WakeupOnCollisionEntry criterion. + * @param armingPath the path used to arm 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 arm 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. + */ + @Override + 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. + */ + @Override + 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; + + 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(); + Vector parents = ((SharedGroupRetained)nodeR).parents; + NodeRetained prevNodeR = nodeR; + for(int i=parents.size()-1; i >=0; i--) { + NodeRetained linkR = parents.get(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; + } + } + + + @Override + 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. + */ + @Override + 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/javax/media/j3d/WakeupOnCollisionExit.java b/src/javax/media/j3d/WakeupOnCollisionExit.java new file mode 100644 index 0000000..775096f --- /dev/null +++ b/src/javax/media/j3d/WakeupOnCollisionExit.java @@ -0,0 +1,394 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * 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 arm 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 arm 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 + * arm 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 + * arm 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 arm collision + * detection + */ + public WakeupOnCollisionExit(Bounds armingBounds) { + this(null, USE_BOUNDS, (Bounds) armingBounds.clone()); + } + + /** + * Constructs a new WakeupOnCollisionExit criterion. + * @param armingPath the path used to arm 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 arm 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. + */ + @Override + 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. + */ + @Override + 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); + } + } + + @Override + 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. + */ + @Override + 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/javax/media/j3d/WakeupOnCollisionMovement.java b/src/javax/media/j3d/WakeupOnCollisionMovement.java new file mode 100644 index 0000000..a4942e3 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnCollisionMovement.java @@ -0,0 +1,402 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * 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 arm 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 arm 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 + * arm 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 + * arm 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 arm collision + * detection + */ + public WakeupOnCollisionMovement(Bounds armingBounds) { + this(null, USE_BOUNDS, (Bounds)armingBounds.clone()); + } + + /** + * Constructs a new WakeupOnCollisionMovement criterion. + * @param armingPath the path used to arm 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 arm 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. + */ + @Override + 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. + */ + @Override + 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); + } + } + + @Override + 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. + */ + @Override + 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/javax/media/j3d/WakeupOnDeactivation.java b/src/javax/media/j3d/WakeupOnDeactivation.java new file mode 100644 index 0000000..91acb03 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnDeactivation.java @@ -0,0 +1,96 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) {} +} diff --git a/src/javax/media/j3d/WakeupOnElapsedFrames.java b/src/javax/media/j3d/WakeupOnElapsedFrames.java new file mode 100644 index 0000000..cb596a4 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnElapsedFrames.java @@ -0,0 +1,181 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + * + *

+ * 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 + * processStimulus 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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) { + this.countdown = this.frameCount; + } + +} diff --git a/src/javax/media/j3d/WakeupOnElapsedTime.java b/src/javax/media/j3d/WakeupOnElapsedTime.java new file mode 100644 index 0000000..1f93bd3 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnElapsedTime.java @@ -0,0 +1,112 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + void addBehaviorCondition(BehaviorStructure bs) { + this.triggeredTime = wait + J3dClock.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. + */ + @Override + 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 + */ + @Override + void reInsertElapseTimeCond() { + super.reInsertElapseTimeCond(); + this.triggeredTime = wait + J3dClock.currentTimeMillis(); + VirtualUniverse.mc.timerThread.add(this); + } + + + /** + * Perform task in addBehaviorCondition() that has to be + * set every time the condition met. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) { + this.triggeredTime = wait + J3dClock.currentTimeMillis(); + VirtualUniverse.mc.timerThread.add(this); + } +} diff --git a/src/javax/media/j3d/WakeupOnElapsedTimeHeap.java b/src/javax/media/j3d/WakeupOnElapsedTimeHeap.java new file mode 100644 index 0000000..d14a148 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnElapsedTimeHeap.java @@ -0,0 +1,226 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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 + */ + @Override + 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(); + } + + } + + + @Override + 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/javax/media/j3d/WakeupOnSensorEntry.java b/src/javax/media/j3d/WakeupOnSensorEntry.java new file mode 100644 index 0000000..4da94dc --- /dev/null +++ b/src/javax/media/j3d/WakeupOnSensorEntry.java @@ -0,0 +1,146 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) {} +} diff --git a/src/javax/media/j3d/WakeupOnSensorExit.java b/src/javax/media/j3d/WakeupOnSensorExit.java new file mode 100644 index 0000000..a7d314b --- /dev/null +++ b/src/javax/media/j3d/WakeupOnSensorExit.java @@ -0,0 +1,145 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) {} +} diff --git a/src/javax/media/j3d/WakeupOnTransformChange.java b/src/javax/media/j3d/WakeupOnTransformChange.java new file mode 100644 index 0000000..2ef17c9 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnTransformChange.java @@ -0,0 +1,96 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +/** + * 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. + */ + @Override + void addBehaviorCondition(BehaviorStructure bs) { + transform.addCondition(this); + } + + + /** + * This is a callback from BehaviorStructure. It is + * used to remove wakeupCondition from behavior structure. + */ + @Override + void removeBehaviorCondition(BehaviorStructure bs) { + transform.removeCondition(this); + } + + + /** + * Perform task in addBehaviorCondition() that has to be + * set every time the condition met. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) {} +} diff --git a/src/javax/media/j3d/WakeupOnViewPlatformEntry.java b/src/javax/media/j3d/WakeupOnViewPlatformEntry.java new file mode 100644 index 0000000..33f3d65 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnViewPlatformEntry.java @@ -0,0 +1,150 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + 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/javax/media/j3d/WakeupOnViewPlatformExit.java b/src/javax/media/j3d/WakeupOnViewPlatformExit.java new file mode 100644 index 0000000..4778ea7 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnViewPlatformExit.java @@ -0,0 +1,152 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +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. + */ + @Override + 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. + */ + @Override + 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. + */ + @Override + 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/javax/media/j3d/WakeupOr.java b/src/javax/media/j3d/WakeupOr.java new file mode 100644 index 0000000..1d4b934 --- /dev/null +++ b/src/javax/media/j3d/WakeupOr.java @@ -0,0 +1,118 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + + +/** + * 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. + *

+ * 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 + */ + @Override + 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. + */ + @Override + 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 + */ + @Override + void cleanTree(BehaviorStructure bs) { + for (int i=0; i + * 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 + */ + @Override + 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. + */ + @Override + 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 + */ + @Override + void cleanTree(BehaviorStructure bs) { + for (int i=0; i + + + + Java 3D API - Behaviors and Interpolators + + +

Behaviors and Interpolators

+

Behavior nodes provide the means for +animating objects, processing keyboard and mouse inputs, reacting to +movement, and enabling and processing pick events. Behavior nodes +contain Java code and state variables. A Behavior node's Java code can +interact with Java objects, change node values within a Java 3D +scene +graph, change the behavior's internal state-in general, perform any +computation it wishes. +

+

Simple behaviors can add surprisingly interesting effects to a scene +graph. For example, one can animate a rigid object by using a Behavior +node to repetitively modify the TransformGroup node that points to the +object one wishes to animate. Alternatively, a Behavior node can track +the current position of a mouse and modify portions of the scene graph +in response.

+

Behavior Object

+

A Behavior leaf node object contains a scheduling region and two +methods: an initialize method called once when the +behavior becomes "live" and a processStimulus +method called whenever appropriate by the Java 3D behavior +scheduler. +The Behavior object also contains the state information needed by its initialize +and processStimulus methods. +

+

The scheduling region defines a spatial volume that serves +to enable the scheduling of Behavior nodes. A Behavior node is active +(can receive stimuli) whenever an active ViewPlatform's activation +volume intersects a Behavior object's scheduling region. Only active +behaviors can receive stimuli. +

+

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 processStimulus 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: +

+
    +
  • All modifications to scene graph objects (not including geometry +by-reference or texture by-reference) made from the processStimulus +method of a single behavior instance are guaranteed to take effect in +the same rendering frame
  • +
+
    +
  • All modifications to scene graph objects (not including geometry +by-reference or texture by-reference) made from the processStimulus +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.
  • +
+

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. +

+

Code Structure

+

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 change only 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. +

+

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. +

+

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: +

+
    +
  • Code to decode and extract references from the WakeupCondition +enumeration that caused the object's awakening.
  • +
+
    +
  • Code to perform the manipulations associated with the +WakeupCondition.
  • +
+
    +
  • Code to establish this behavior's new WakeupCondition.
  • +
+
    +
  • A path to Exit (so that execution returns to the Java 3D +behavior +scheduler).
  • +
+

WakeupCondition Object

+

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. +

+

+

+

WakeupCriterion Object

+

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 +

+
    +
  • The center of an active ViewPlatform enters a specified region.
  • +
+
    +
  • The center of an active ViewPlatform exits a specified region.
  • +
+
    +
  • A behavior is activated.
  • +
+
    +
  • A behavior is deactivated.
  • +
+
    +
  • A specified TransformGroup node's transform changes.
  • +
+
    +
  • Collision is detected between a specified Shape3D node's Geometry +object and any other object.
  • +
+
    +
  • Movement occurs between a specified Shape3D node's Geometry +object and any other object with which it collides.
  • +
+
    +
  • A specified Shape3D node's Geometry object no longer collides +with any other object.
  • +
+
    +
  • A specified Behavior object posts a specific event.
  • +
+
    +
  • A specified AWT event occurs.
  • +
+
    +
  • A specified time interval elapses.
  • +
+
    +
  • A specified number of frames have been drawn.
  • +
+
    +
  • The center of a specified Sensor enters a specified region.
  • +
+
    +
  • The center of a specified Sensor exits a specified region.
  • +
+

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. +

+

Composing WakeupCriterion +Objects

+

A Behavior object can combine multiple WakeupCriterion objects into +a +more powerful, composite WakeupCondition. Java 3D behaviors +construct a +composite WakeupCondition in one of the following ways: +

+
    +
  • WakeupAnd: An array of +WakeupCriterion objects ANDed together.
  • +
+
            WakeupCriterion && WakeupCriterion && ...
+
    +
  • WakeupOr: An array of +WakeupCriterion objects ORed together.
  • +
+
            WakeupCriterion || WakeupCriterion || ...
+
    +
  • WakeupAndOfOrs: An array of +WakeupOr WakeupCondition objects that +are then ANDed together.
  • +
+
            WakeupOr && WakeupOr && ...
+
    +
  • WakeupOrOfAnds: An array of +WakeupAnd WakeupCondition objects +that are then ORed together.
  • +
+
            WakeupAnd || WakeupAnd || ...
+

Composing Behaviors

+

Behavior objects can condition themselves to awaken only when +signaled +by another Behavior node. The WakeupOnBehaviorPost +WakeupCriterion +takes as arguments a reference to a Behavior node and an integer. These +two arguments allow a behavior to limit its wakeup criterion to a +specific post by a specific behavior. +

+

The WakeupOnBehaviorPost WakeupCriterion permits behaviors to chain +their computations, allowing parenthetical computations-one behavior +opens a door and the second closes the same door, or one behavior +highlights an object and the second unhighlights the same object. +

+

+

+

Scheduling

+

As a virtual universe grows large, Java 3D must carefully +husband +its +resources to ensure adequate performance. In a 10,000-object virtual +universe with 400 or so Behavior nodes, a naive implementation of Java +3D could easily end up consuming the majority of its compute cycles in +executing the behaviors associated with the 400 Behavior objects before +it draws a frame. In such a situation, the frame rate could easily drop +to unacceptable levels. +

+

Behavior objects are usually associated with geometric objects in +the +virtual universe. In our example of 400 Behavior objects scattered +throughout a 10,000-object virtual universe, only a few of these +associated geometric objects would be visible at a given time. A +sizable fraction of the Behavior nodes-those associated with nonvisible +objects-need not be executed. Only those relatively few Behavior +objects that are associated with visible objects must be executed. +

+

Java 3D mitigates the problem of a large number of Behavior +nodes in +a +high-population virtual universe through execution culling-choosing to +invoke only those behaviors that have high relevance. +

+

Java 3D requires each behavior to have a scheduling region +and to post a wakeup condition. Together a behavior's scheduling region +and wakeup condition provide Java 3D's behavior scheduler with +sufficient domain knowledge to selectively prune behavior invocations +and invoke only those behaviors that absolutely need to be executed. +

+

+

+

How Java 3D Performs +Execution Culling

+

Java 3D finds all scheduling regions associated with Behavior +nodes +and +constructs a scheduling/volume tree. It also creates an AND/OR tree +containing all the Behavior node wakeup criteria. These two data +structures provide the domain knowledge Java 3D needs to prune +unneeded +behavior execution (to perform "execution triage"). +

+

Java 3D must track a behavior's wakeup conditions only if an +active +ViewPlatform object's activation volume intersects with that Behavior +object's scheduling region. If the ViewPlatform object's activation +volume does not intersect with a behavior's scheduling region, +Java 3D +can safely ignore that behavior's wakeup criteria. +

+

In essence, the Java 3D scheduler performs the following +checks: +

+
    +
  • Find all Behavior objects with scheduling regions that intersect +the active ViewPlatform object's activation volume.
  • +
+
    +
  • For each Behavior object within the ViewPlatform's activation +volume, if that behavior's WakeupCondition is true, +schedule that Behavior object for execution.
  • +
+

Java 3D's behavior scheduler executes those Behavior objects +that +have +been scheduled by calling the behavior's processStimulus +method. +

+

Interpolator Behaviors

+

This section describes Java 3D's predefined Interpolator behaviors. +They are called interpolators +because they smoothly interpolate between the two extreme values that +an interpolator can produce. Interpolators perform simple behavioral +acts, yet they provide broad functionality. +

+

The Java 3D API provides interpolators for a number of +functions: +manipulating transforms within a TransformGroup, modifying the values +of a Switch node, and modifying Material attributes such as color and +transparency. +

+

These predefined Interpolator behaviors share the same mechanism for +specifying and later for converting a temporal value into an alpha +value. Interpolators consist of two portions: a generic portion that +all interpolators share and a domain-specific portion. +

+

The generic portion maps time in milliseconds onto a value in the +range +[0.0, 1.0] inclusive. The domain-specific portion maps an alpha value +in the range [0.0, 1.0] onto a value appropriate to the predefined +behavior's range of outputs. An alpha value of 0.0 generates an +interpolator's minimum value, an alpha value of 1.0 generates an +interpolator's maximum value, and an alpha value somewhere in between +generates a value proportionally in between the minimum and maximum +values. +

+

Mapping Time to Alpha

+

Several parameters control the mapping of time onto an alpha value +(see +the javadoc for the Alpha object for a +description of the API). +That mapping is deterministic as long as its parameters do not change. +Thus, two different interpolators with the same parameters will +generate the same alpha value given the same time value. This means +that two interpolators that do not communicate can still precisely +coordinate their activities, even if they reside in different threads +or even different processors-as long as those processors have +consistent clocks. +

+

Figure +1 +shows the components of an interpolator's time-to-alpha mapping. Time +is represented on the horizontal axis. Alpha is represented on the +vertical axis. As we move from left to right, we see the alpha value +start at 0.0, rise to 1.0, and then decline back to 0.0 on the +right-hand side. +

+

On the left-hand side, the trigger time defines +when this interpolator's waveform begins in milliseconds. The region +directly to the right of the trigger time, labeled Phase Delay, defines +a time period where the waveform does not change. During phase delays +alpha is either 0 or 1, depending on which region it precedes. +

+

Phase delays provide an important means for offsetting multiple +interpolators from one another, especially where the interpolators have +all the same parameters. The next four regions, labeled α +increasing, α at 1, α decreasing, and +α at 0, all specify durations for +the corresponding values +of alpha. +

+

Interpolators have a loop count that determines how many times to +repeat the sequence of alpha increasing, alpha at 1, alpha decreasing, +and alpha at 0; they also have associated mode flags that enable either +the increasing or decreasing portions, or both, of the waveform. +

+

Time-to-Alpha Mapping +

+

+

+
    + Figure 1 – An Interpolator's Generic +Time-to-Alpha Mapping Sequence +
+

+Developers can use the loop count in conjunction with the mode flags to +generate various kinds of actions. Specifying a loop count of 1 and +enabling the mode flag for only the alpha-increasing and alpha-at-1 +portion of the waveform, we would get the waveform shown in Figure +2. +

+

Alpha Increasing +

+

+

+
    + Figure 2 – An Interpolator Set to a Loop +Count of 1 with Mode Flags Set to Enable +Only the Alpha-Increasing and Alpha-at-1 Portion of the Waveform +
+

+In Figure +2, +the alpha value is 0 before the combination of trigger time plus the +phase delay duration. The alpha value changes from 0 to 1 over a +specified interval of time, and thereafter the alpha value remains 1 +(subject to the reprogramming of the interpolator's parameters). A +possible use of a single alpha-increasing value might be to combine it +with a rotation interpolator to program a door opening. +

+

Similarly, by specifying a loop count of 1 and +a mode flag that enables only the alpha-decreasing and alpha-at-0 +portion of the waveform, we would get the waveform shown in Figure +3. +

+

In Figure +3, +the alpha value is 1 before the combination of trigger time plus the +phase delay duration. The alpha value changes from 1 to 0 over a +specified interval; thereafter the alpha value remains 0 (subject to +the reprogramming of the interpolator's parameters). A possible use of +a single α-decreasing value might be to combine it with a +rotation +interpolator to program a door closing. +

+

Alpha Decreasing +

+

+

+
    + Figure 3 – An Interpolator Set to a Loop +Count of 1 with Mode Flags Set to Enable +Only the Alpha-Decreasing and Alpha-at-0 Portion of the Waveform +
+

+We can combine both of the above waveforms by specifying a loop count +of 1 and setting the mode flag to enable both the alpha-increasing and +alpha-at-1 portion of the waveform as well as the alpha-decreasing and +alpha-at-0 portion of the waveform. This combination would result in +the waveform shown in Figure +4. +

+

Alpha Increasing & Decreasing +

+

+

+
    + Figure 4 – An Interpolator Set to a Loop +Count of 1 with Mode Flags +Set to Enable All Portions of the Waveform +
+

+In Figure +4, +the alpha value is 0 before the combination of trigger time plus the +phase delay duration. The alpha value changes from 0 to 1 over a +specified period of time, remains at 1 for another specified period of +time, then changes from 1 to 0 over a third specified period of time; +thereafter the alpha value remains 0 (subject to the reprogramming of +the interpolator's parameters). A possible use of an alpha-increasing +value followed by an alpha-decreasing value might be to combine it with +a rotation interpolator to program a door swinging open and then +closing. +

+

By increasing the loop count, we can get +repetitive behavior, such as a door swinging open and closed some +number of times. At the extreme, we can specify a loop count of -1 +(representing infinity). +

+

We can construct looped versions of the waveforms shown in Figure +2, Figure +3, and Figure +4. Figure +5 shows a looping interpolator with mode flags set to enable +only the alpha-increasing and alpha-at-1 portion of the waveform. +

+

Alpha Increasing Infinite Loop +

+

+

+
    + Figure 5 – An Interpolator Set to Loop +Infinitely and Mode Flags Set to Enable +Only the Alpha-Increasing and Alpha-at-1 Portion of the Waveform +
+

+In Figure +5, alpha goes from 0 to 1 over a fixed duration of time, stays +at 1 for another fixed duration of time, and then repeats. +

+

Similarly, Figure +6 shows a looping interpolator with mode flags set to enable +only the alpha-decreasing and alpha-at-0 portion of the waveform. +

+

Alpha Decreasing Infinite Loop +

+

+

+
    + Figure 6 – An Interpolator Set to Loop +Infinitely and Mode Flags Set to Enable +Only the Alpha-Decreasing and Alpha-at-0 Portion of the Waveform +
+

+Finally, Figure +7 shows a looping interpolator with both the increasing and +decreasing portions of the waveform enabled. +

+

In all three cases shown by Figure +5, Figure +6, and Figure +7, we can compute the exact value of alpha at any point in time. +

+

Alpha Increasing & Decreasing  Infinite Loop +

+

+

+
    + Figure 7 – An Interpolator Set to Loop +Infinitely and Mode Flags Set +to Enable All Portions of the Waveform +
+

+Java 3D's preprogrammed behaviors permit other behaviors to change +their parameters. When such a change occurs, the alpha value changes to +match the state of the newly parameterized interpolator. +

+

Acceleration of Alpha

+

Commonly, developers want alpha to change slowly at first and then +to +speed up until the change in alpha reaches some appropriate rate. This +is analogous to accelerating your car up to the speed limit-it does not +start off immediately at the speed limit. Developers specify this +"ease-in, ease-out" behavior through two additional parameters, the increasingAlphaRampDuration +and the decreasing-AlphaRampDuration. +

+

Each of these parameters specifies a period within the increasing or +decreasing alpha duration region during which the "change in alpha" is +accelerated (until it reaches its maximum per-unit-of-time step size) +and then symmetrically decelerated. Figure +8 shows three general examples of how the increasingAlphaRampDuration +method can be used to modify the alpha waveform. A value of 0 for the +increasing ramp duration implies that α +is not accelerated; it changes at a constant rate. A value of 0.5 or +greater (clamped to 0.5) for this increasing ramp duration implies that +the change in α is accelerated during the first half of the +period and +then decelerated during the second half of the period. For a value of n +that is less than 0.5, alpha is accelerated for duration n, +held constant for duration (1.0 - 2n), then decelerated for +duration n of the period. +

+

Alpha acceleration +

+

+

+
    + Figure 8 – How an Alpha-Increasing Waveform +Changes with Various +Values of increasing-AlphaRampDuration +
+ + diff --git a/src/javax/media/j3d/doc-files/Behaviors1.gif b/src/javax/media/j3d/doc-files/Behaviors1.gif new file mode 100644 index 0000000..bb288ce Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors1.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors2.gif b/src/javax/media/j3d/doc-files/Behaviors2.gif new file mode 100644 index 0000000..005564f Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors2.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors3.gif b/src/javax/media/j3d/doc-files/Behaviors3.gif new file mode 100644 index 0000000..a8beb09 Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors3.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors4.gif b/src/javax/media/j3d/doc-files/Behaviors4.gif new file mode 100644 index 0000000..685bcb7 Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors4.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors5.gif b/src/javax/media/j3d/doc-files/Behaviors5.gif new file mode 100644 index 0000000..74783fb Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors5.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors6.gif b/src/javax/media/j3d/doc-files/Behaviors6.gif new file mode 100644 index 0000000..8614a4e Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors6.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors7.gif b/src/javax/media/j3d/doc-files/Behaviors7.gif new file mode 100644 index 0000000..0f2ce48 Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors7.gif differ diff --git a/src/javax/media/j3d/doc-files/Behaviors8.gif b/src/javax/media/j3d/doc-files/Behaviors8.gif new file mode 100644 index 0000000..d048cfa Binary files /dev/null and b/src/javax/media/j3d/doc-files/Behaviors8.gif differ diff --git a/src/javax/media/j3d/doc-files/Concepts.html b/src/javax/media/j3d/doc-files/Concepts.html new file mode 100644 index 0000000..7b005af --- /dev/null +++ b/src/javax/media/j3d/doc-files/Concepts.html @@ -0,0 +1,291 @@ + + + + + Java 3D API - Concepts + + +

Java 3D Concepts

+

The Java 3D API specification serves to define objects, methods, and +their actions precisely. Describing how to use an API belongs in a +tutorial or programmer's +reference manual, and is well beyond the scope of this specification. +However, a short introduction to the main concepts in Java 3D will +provide the context for understanding the detailed, but isolated, +specification found in the class and method descriptions. We introduce +some of the key Java 3D concepts and illustrate them with some simple +program fragments. +

+

+

+

Basic Scene Graph Concepts

+

A scene graph is a "tree" structure that contains data arranged in a +hierarchical manner. The scene graph consists of parent nodes, child +nodes, and data objects. The parent nodes, called Group nodes, organize +and, in some cases, control how Java 3D interprets their descendants. +Group nodes serve as the glue that holds a scene graph together. Child +nodes can be either Group nodes or Leaf nodes. Leaf nodes have no +children. They encode the core semantic elements of a scene graph- for +example, what to draw (geometry), what to play (audio), how to +illuminate objects (lights), or what code to execute (behaviors). Leaf +nodes refer to data objects, called NodeComponent objects. +NodeComponent objects are not scene graph nodes, but they contain the +data that Leaf nodes require, such as the geometry to draw or the sound +sample to play. +

+

A Java 3D application builds and manipulates a scene graph by +constructing Java 3D objects and then later modifying those objects by +using their methods. A Java 3D program first constructs a scene graph, +then, once built, hands that scene graph to Java 3D for processing. +

+

The structure of a scene graph determines the relationships among +the +objects in the graph and determines which objects a programmer can +manipulate as a single entity. Group nodes provide a single point for +handling or manipulating all the nodes beneath it. A programmer can +tune a scene graph appropriately by thinking about what manipulations +an application will need to perform. He or she can make a particular +manipulation easy or difficult by grouping or regrouping nodes in +various ways. +

+

+

+

Constructing a Simple Scene +Graph

+

The following code constructs a simple scene graph consisting of a +group node and two leaf +nodes.
+

+

+Listing 1 – Code for Constructing a Simple Scene Graph +

+
+
Shape3D myShape1 = new Shape3D(myGeometry1, myAppearance1);
Shape3D myShape2 = new Shape3D(myGeometry2);
myShape2.setAppearance(myAppearance2);

Group myGroup = new Group();
myGroup.addChild(myShape1);
myGroup.addChild(myShape2);
+
+

It first constructs one leaf node, the first of two Shape3D +nodes, using a constructor that takes both a Geometry and an Appearance +NodeComponent object. It then constructs the second Shape3D node, with +only a Geometry object. Next, since the second Shape3D node was created +without an Appearance object, it supplies the missing Appearance object +using the Shape3D node's setAppearance method. At this +point both leaf nodes have been fully constructed. The code next +constructs a group node to hold the two leaf nodes. It +uses the Group node's addChild method to add the two leaf +nodes as children to the group node, finishing the construction of the +scene graph. Figure +1 +shows the constructed scene graph, all the nodes, the node component +objects, and the variables used in constructing the scene graph. +

+

A Simple Scene Graph +

+
    + Figure 1 – A Simple Scene Graph +
+

A Place For Scene Graphs

+Once a scene graph has been constructed, the +question becomes what to do with it? Java 3D cannot start rendering a +scene graph until a program "gives" it the scene graph. The program +does this by inserting the scene graph into the virtual universe. +

Java 3D places restrictions on how a program can insert a scene +graph +into a universe. +

+

A Java 3D environment consists of two superstructure objects, +VirtualUniverse and Locale, and one or more graphs, rooted by a special +BranchGroup node. Figure 2 shows these objects +in context with other scene graph objects. +

+

The VirtualUniverse object defines a universe. A universe allows a +Java +3D program to create a separate and distinct arena for defining objects +and their relationships to one another. Typically, Java 3D programs +have only one VirtualUniverse object. Programs that have more than one +VirtualUniverse may share NodeComponent objects but not scene graph +node objects. +

+

The Locale object specifies a fixed position within the universe. +That +fixed position defines an origin for all scene graph nodes beneath it. +The Locale object allows a programmer to specify that origin very +precisely and with very high dynamic range. A Locale can accurately +specify a location anywhere in the known physical universe and at the +precision of Plank's distance. Typically, Java 3D programs have only +one Locale object with a default origin of (0, 0, 0). Programs that +have more than one Locale object will set the location of the +individual Locale objects so that they provide an appropriate local +origin for the nodes beneath them. For example, to model the Mars +landing, a programmer might create one Locale object with an origin at +Cape Canaveral and another with an origin located at the landing site +on Mars. +

+

Content Branch, View Branch, Superstructure +

+
    + Figure 2 – Content Branch, View Branch, and +Superstructure +
+

+The BranchGroup node serves as the root of a branch graph. +Collectively, the BranchGroup node and all of its children form the +branch graph. The two kinds of branch graphs are called content +branches and view branches. A content branch contains only +content-related leaf nodes, while a view branch +contains a ViewPlatform leaf node and may contain other content-related +leaf nodes. Typically, a universe contains more than one branch +graph-one view branch, and any number of content branches. +

+

Besides serving as the root of a branch graph, the BranchGroup node +has +two special properties: It alone may be inserted into a Locale object, +and it may be compiled. Java 3D treats uncompiled and compiled branch +graphs identically, though compiled branch graphs will typically render +more efficiently. +

+

We could not insert the scene graph created by our simple example (Listing +1) into a Locale because it does not have a BranchGoup node for +its root. Listing 2 +shows a modified version of our first code example that creates a +simple content branch graph and the minimum of superstructure objects. +Of special note, Locales do not have children, and they are not part of +the scene graph. The method for inserting a branch graph is addBranchGraph, +whereas addChild is the method for adding children to all +group nodes.

+

+Listing 2 – Code for Constructing a +Scene Graph and Some +Superstructure Objects +

+
+
Shape3D myShape1 = new Shape3D(myGeometry1, myAppearance1);
Shape3D myShape2 = new Shape3D(myGeometry2, myAppearance2);

BranchGroup myBranch = new BranchGroup();
myBranch.addChild(myShape1);
myBranch.addChild(myShape2);
myBranch.compile();

VirtualUniverse myUniverse = new VirtualUniverse();
Locale myLocale = new Locale(myUniverse);
myLocale.addBranchGraph(myBranch);
+
+

SimpleUniverse Utility

+Most Java 3D programs build an identical set of superstructure and view +branch objects, so the Java 3D utility packages provide a universe +package for constructing and manipulating the objects in a view branch. +The classes in the universe package provide a quick means +for building a single view (single window) application. Listing 3 +shows a code fragment for using the SimpleUniverse class. Note that the +SimpleUniverse constructor takes a Canvas3D as an argument, in this +case referred to by the variable myCanvas. +

Listing 3 – Code +for Constructing a Scene Graph Using the Universe +Package +

+
+
import com.sun.j3d.utils.universe.*;

Shape3D myShape1 = new Shape3D(myGeometry1, myAppearance1);
Shape3D myShape2 = new Shape3D(myGeometry2, myAppearance2);

BranchGroup myBranch = new BranchGroup();
myBranch.addChild(myShape1);
myBranch.addChild(myShape2);
myBranch.compile();

SimpleUniverse myUniv = new SimpleUniverse(myCanvas);
myUniv.addBranchGraph(myBranch);
+
+

Processing a Scene Graph

+When given a scene graph, Java 3D processes that scene graph as +efficiently as possible. How a Java 3D implementation processes a scene +graph can vary, as long as the implementation conforms to the semantics +of the API. In general, a Java 3D implementation will render all +visible objects, play all enabled sounds, execute all triggered +behaviors, process any identified input devices, and check for and +generate appropriate collision events. +

The order that a particular Java 3D implementation renders objects +onto +the display is carefully not defined. One implementation might render +the first Shape3D object and then the second. Another might first +render the second Shape3D node before it renders the first one. Yet +another implementation may render both Shape3D nodes in parallel. +

+

+

+

Features of Java 3D

+Java 3D allows a programmer to specify a broad range of information. It +allows control over the shape of objects, their color, and +transparency. It allows control over background effects, lighting, and +environmental effects such as fog. It allows control over the placement +of all objects (even nonvisible objects such as lights and behaviors) +in the scene graph and over their orientation and scale. It allows +control over how those objects move, rotate, stretch, shrink, or morph +over time. It allows control over what code should execute, what sounds +should play, and how they should sound and change over time. +

Java 3D provides different techniques for controlling the effect of +various features. Some techniques act fairly locally, such as getting +the color of a vertex. Other techniques have broader influence, such as +changing the color or appearance of an entire object. Still other +techniques apply to a broad number of objects. In the first two cases, +the programmer can modify a particular object or an object associated +with the affected object. In the latter case, Java 3D provides a means +for specifying more than one object spatially. +

+

+

+

Bounds

+Bounds objects allow a programmer to define a volume in space. There +are three ways to specify this volume: as a box, a sphere, or a set of +planes enclosing a space. +

Bounds objects specify a volume in which particular operations +apply. +Environmental effects such as lighting, fog, alternate appearance, and +model clipping planes use bounds objects to specify their region of +influence. Any object that falls within the space defined by the bounds +object has the particular environmental effect applied. The proper use +of bounds objects can ensure that these environmental effects are +applied only to those objects in a particular volume, such as a light +applying only to the objects within a single room. +

+

Bounds objects are also used to specify a region of action. +Behaviors +and sounds execute or play only if they are close enough to the viewer. +The use of behavior and sound bounds objects allows Java 3D to cull +away those behaviors and sounds that are too far away to affect the +viewer (listener). By using bounds properly, a programmer can ensure +that only the relevant behaviors and sounds execute or play. +

+

Finally, bounds objects are used to specify a region of application +for +per-view operations such as background, clip, and soundscape selection. +For example, the background node whose region of application is closest +to the viewer is selected for a given view. +

+

+

+

Nodes

+All scene graph nodes have an implicit location in space of (0, 0, 0). +For objects that exist in space, this implicit location provides a +local coordinate system for that object, a fixed reference point. Even +abstract objects that may not seem to have a well-defined location, +such as behaviors and ambient lights, have this implicit location. An +object's location provides an origin for its local coordinate system +and, just as importantly, an origin for any bounding volume information +associated with that object. +

Live and/or Compiled

+All scene graph objects, including nodes and node component objects, +are either part of an active universe or not. An object is said to be live +if it is part of an active universe. Additionally, branch graphs are +either compiled +or not. When a node is either live or compiled, Java 3D enforces access +restrictions to nodes and node component objects. Java 3D allows only +those operations that are enabled by the program before a node or node +component becomes live or is compiled. It is best to set capabilities +when you build your content. Listing 4 shows +an example where we create a TransformGroup node and +enable it for writing. +

Listing 4 – +Capabilities Example +

+
+
TransformGroup myTrans = new TransformGroup();
myTrans.setCapability(Transform.ALLOW_TRANSFORM_WRITE);
+
+

By setting the capability to write the transform, Java 3D will allow +the following code to execute: +

+
myTrans.setTransform3D(myT3D);
+

It is important to ensure that all needed capabilities are set and +that +unnecessary capabilities are not set. The process of compiling a branch +graph examines the capability bits and uses that information to reduce +the amount of computation needed to run a program. +

+ + diff --git a/src/javax/media/j3d/doc-files/Concepts1.gif b/src/javax/media/j3d/doc-files/Concepts1.gif new file mode 100644 index 0000000..8aa0dbc Binary files /dev/null and b/src/javax/media/j3d/doc-files/Concepts1.gif differ diff --git a/src/javax/media/j3d/doc-files/Concepts2.gif b/src/javax/media/j3d/doc-files/Concepts2.gif new file mode 100644 index 0000000..f21e085 Binary files /dev/null and b/src/javax/media/j3d/doc-files/Concepts2.gif differ diff --git a/src/javax/media/j3d/doc-files/DAG.gif b/src/javax/media/j3d/doc-files/DAG.gif new file mode 100644 index 0000000..8479136 Binary files /dev/null and b/src/javax/media/j3d/doc-files/DAG.gif differ diff --git a/src/javax/media/j3d/doc-files/HelloUniverse.html b/src/javax/media/j3d/doc-files/HelloUniverse.html new file mode 100644 index 0000000..5e37bd6 --- /dev/null +++ b/src/javax/media/j3d/doc-files/HelloUniverse.html @@ -0,0 +1,21 @@ + + + + + HelloUniverse + + +

HelloUniverse: A Sample Java +3D Program

+

Here are code fragments from a simple program, HelloUniverse.java, +that creates a cube and a RotationInterpolator behavior object that +rotates the cube at a constant rate of pi/2 radians per second. The +HelloUniverse class creates the branch graph +that includes the cube and the RotationInterpolator behavior. It then +adds this branch graph to the Locale object generated by the +SimpleUniverse utility. +

+


public class HelloUniverse ... {
public BranchGroup createSceneGraph() {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup();

// Create the TransformGroup node and initialize it to the
// identity. Enable the TRANSFORM_WRITE capability so that
// our behavior code can modify it at run time. Add it to
// the root of the subgraph.
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(
TransformGroup.ALLOW_TRANSFORM_WRITE);
objRoot.addChild(objTrans);

// Create a simple Shape3D node; add it to the scene graph.
objTrans.addChild(new ColorCube(0.4));

// Create a new Behavior object that will perform the
// desired operation on the specified transform and add
// it into the scene graph.
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, 4000);
RotationInterpolator rotator = new RotationInterpolator(
rotationAlpha, objTrans, yAxis,
0.0f, (float) Math.PI*2.0f);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
rotator.setSchedulingBounds(bounds);
objRoot.addChild(rotator);

// Have Java 3D perform optimizations on this scene graph.
objRoot.compile();

return objRoot;
}

public HelloUniverse() {
<set layout of container, construct canvas3d, add canvas3d>

// Create the scene; attach it to the virtual universe
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(canvas3d);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
}
+ + diff --git a/src/javax/media/j3d/doc-files/Immediate.html b/src/javax/media/j3d/doc-files/Immediate.html new file mode 100644 index 0000000..101fe22 --- /dev/null +++ b/src/javax/media/j3d/doc-files/Immediate.html @@ -0,0 +1,114 @@ + + + + + Java 3D API - Immediate-Mode Rendering + + +

Immediate-Mode Rendering

+

Java 3D is fundamentally a scene graph-based API. Most of +the constructs in the API are biased toward retained mode and +compiled-retained mode rendering. However, there are some applications +that want both the control and the flexibility that immediate-mode +rendering offers. +

+

Immediate-mode applications can either use or ignore Java 3D's +scene +graph structure. By using immediate mode, end-user applications have +more freedom, but this freedom comes at the expense of performance. In +immediate mode, Java 3D has no high-level information concerning +graphical objects or their composition. Because it has minimal global +knowledge, Java 3D can perform only localized optimizations on +behalf +of the application programmer. +

+

+

+

Two Styles of Immediate-Mode +Rendering

+Use of Java 3D's immediate mode falls into one of two categories: +pure +immediate-mode rendering and mixed-mode rendering in which immediate +mode and retained or compiled-retained mode interoperate and render to +the same canvas. The Java 3D renderer is idle in pure immediate +mode, +distinguishing it from mixed-mode rendering. +

Pure Immediate-Mode +Rendering

+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. +
Note: Scene antialiasing is not supported +in pure immediate mode. +
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, finally, a ViewPlatform leaf node object that defines +the position and orientation within the virtual universe that generates +the view (see Figure +1). +

Minimal Immediate-Mode Structure

+

+

+
    + Figure 1 – Minimal Immediate-Mode Structure +
+

+Java 3D provides utility functions that create much of this +structure +on behalf of a pure immediate-mode application, making it less +noticeable from the application's perspective-but the structure must +exist. +

+

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 stopRenderer() +method, prior to adding the Canvas3D object to an active View object +(that is, one that is attached to a live ViewPlatform object). +

+

+

+

Mixed-Mode Rendering

+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. +

The basic Java 3D stereo rendering loop, executed for +each +Canvas3D, is as follows: +

+


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


+The basic Java 3D monoscopic rendering loop is as +follows: +


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


+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. + + diff --git a/src/javax/media/j3d/doc-files/Immediate1.gif b/src/javax/media/j3d/doc-files/Immediate1.gif new file mode 100644 index 0000000..2d549b1 Binary files /dev/null and b/src/javax/media/j3d/doc-files/Immediate1.gif differ diff --git a/src/javax/media/j3d/doc-files/Rendering.html b/src/javax/media/j3d/doc-files/Rendering.html new file mode 100644 index 0000000..7415ce8 --- /dev/null +++ b/src/javax/media/j3d/doc-files/Rendering.html @@ -0,0 +1,148 @@ + + + + + Java 3D API - Execution and Rendering Model + + +

Execution and Rendering Model

+

Java 3D's execution and rendering model assumes the +existence of a VirtualUniverse +object and an attached scene graph. This +scene graph can be minimal and not noticeable from an application's +perspective when using immediate-mode rendering, but it must exist. +

+

Java 3D's execution model intertwines with its rendering modes +and +with +behaviors and their scheduling. This chapter first describes the three +rendering modes, then describes how an application starts up a +Java 3D +environment, and finally it discusses how the various rendering modes +work within this framework. +

+

+

+

Three Major Rendering Modes

+

Java 3D supports three different modes for rendering scenes: +immediate +mode, retained mode, and compiled-retained mode. These three levels of +API support represent a potentially large variation in graphics +processing speed and in on-the-fly restructuring. +

+

+

Immediate Mode

+

Immediate mode allows maximum flexibility at some cost in rendering +speed. The application programmer can either use or ignore the scene +graph structure inherent in Java 3D's design. The programmer can +choose +to draw geometry directly or to define a scene graph. Immediate mode +can be either used independently or mixed with retained and/or +compiled-retained mode rendering. The immediate-mode API is described +in the "Immediate-Mode Rendering" section.

+

+

+

Retained Mode

+

Retained mode allows a great deal of the flexibility provided by +immediate mode while also providing a substantial increase in rendering +speed. All objects defined in the scene graph are accessible and +manipulable. The scene graph itself is fully manipulable. The +application programmer can rapidly construct the scene graph, create +and delete nodes, and instantly "see" the effect of edits. Retained +mode also allows maximal access to objects through a general pick +capability. +

+

Java 3D's retained mode allows a programmer to construct +objects, +insert objects into a database, compose objects, and add behaviors to +objects. +

+

In retained mode, Java 3D knows that the programmer has defined +objects, knows how the programmer has combined those objects into +compound objects or scene graphs, and knows what behaviors or actions +the programmer has attached to objects in the database. This knowledge +allows Java 3D to perform many optimizations. It can construct +specialized data structures that hold an object's geometry in a manner +that enhances the speed at which the Java 3D system can render it. +It +can compile object behaviors so that they run at maximum speed when +invoked. It can flatten transformation manipulations and state changes +where possible in the scene graph. +

+

+

+

Compiled-Retained Mode

+

Compiled-retained mode allows the Java 3D API to perform an +arbitrarily +complex series of optimizations including, but not restricted to, +geometry compression, scene graph flattening, geometry grouping, and +state change clustering. +

+

Compiled-retained mode provides hooks for end-user manipulation and +picking. Pick operations return the closest object (in scene graph +space) associated with the picked geometry. +

+

Java 3D's compiled-retained mode ensures effective graphics +rendering +speed in yet one more way. A programmer can request that Java 3D +compile an object or a scene graph. Once it is compiled, the programmer +has minimal access to the internal structure of the object or scene +graph. Capability flags provide access to specified components that the +application program may need to modify on a continuing basis. +

+

A compiled object or scene graph consists of whatever internal +structures Java 3D wishes to create to ensure that objects or +scene +graphs render at maximal rates. Because Java 3D knows that the +majority +of the compiled object's or scene graph's components will not change, +it can perform an extraordinary number of optimizations, including the +fusing of multiple objects into one conceptual object, turning an +object into compressed geometry or even breaking an object up into +like-kind components and reassembling the like-kind components into new +"conceptual objects." +

+

+

+

Instantiating the Render Loop

+

From an application's perspective, Java 3D's render loop runs +continuously. Whenever an application adds a scene branch to the +virtual world, that scene branch is instantly visible. This high-level +view of the render loop permits concurrent implementations of +Java 3D +as well as serial implementations. The remainder of this section +describes the Java 3D render loop bootstrap process from a +serialized +perspective. Differences that would appear in concurrent +implementations are noted as well. +

+

+

An Application-Level +Perspective

+

First the application must construct its scene graphs. It does this +by +constructing scene graph nodes and component objects and linking them +into self-contained trees with a BranchGroup node as a root. The +application next must obtain a reference to any constituent nodes or +objects within that branch that it may wish to manipulate. It sets the +capabilities of all the objects to match their anticipated use and only +then compiles the branch using the BranchGroup's compile +method. Whether it compiles the branch, the application can add it to +the virtual universe by adding the BranchGroup to a Locale object. The +application repeats this process for each branch it wishes to create. +Note that for concurrent Java 3D implementations, whenever an +application adds a branch to the active virtual universe, that branch +becomes visible. +

+

+

Retained and +Compiled-Retained Rendering Modes

+

This initialization process is identical for retained and +compiled-retained modes. In both modes, the application builds a scene +graph. In compiled-retained mode, the application compiles the scene +graph. Then the application inserts the (possibly compiled) scene graph +into the virtual universe. +

+ + diff --git a/src/javax/media/j3d/doc-files/SceneGraphOverview.html b/src/javax/media/j3d/doc-files/SceneGraphOverview.html new file mode 100644 index 0000000..f1616df --- /dev/null +++ b/src/javax/media/j3d/doc-files/SceneGraphOverview.html @@ -0,0 +1,226 @@ + + + + + Java 3D API - Scene Graph Overview + + +

Scene Graph Basics

+

A scene graph consists of Java 3D +objects, called nodes, +arranged in a tree structure. The user creates one or more scene +subgraphs and attaches them to a virtual universe. The individual +connections between Java 3D nodes always represent a directed +relationship: parent to child. Java 3D restricts scene graphs in one +major way: Scene graphs may not contain cycles. Thus, a Java 3D scene +graph is a directed acyclic graph (DAG). See Figure +1. +

+

Java 3D refines the Node object class +into two subclasses: Group +and +Leaf node objects. Group node objects group +together one or more child +nodes. A group node can point to zero or more children but can have +only one parent. The SharedGroup node cannot have any parents (although +it allows sharing portions of a scene graph, as described in "Reusing Scene Graphs"). +Leaf node objects contain the actual definitions of shapes (geometry), +lights, fog, sounds, and so forth. A leaf node has no children and only +one parent. The semantics of the various group and leaf nodes are +described in subsequent chapters.

+

Scene Graph Structure

+

A scene graph organizes and controls the rendering +of its constituent objects. The Java 3D renderer draws a scene graph in +a consistent way that allows for concurrence. The Java 3D renderer can +draw one object independently of other objects. Java 3D can allow such +independence because its scene graphs have a particular form and cannot +share state among branches of a tree. +

+

Spatial Separation

+

The hierarchy of the scene graph encourages a natural spatial +grouping +on the geometric objects found at the leaves of the graph. Internal +nodes act to group their children together. A group node also defines a +spatial bound that contains all the geometry defined by its +descendants. Spatial grouping allows for efficient implementation of +operations such as proximity detection, collision detection, view +frustum culling, and occlusion culling. +

+

Directed Acyclic Graph

+

+
    + Figure 1 – A Java +3D Scene Graph Is a DAG +(Directed Acyclic Graph) +
+

+

State Inheritance

+

A leaf node's state is defined by the nodes in a direct path between +the scene graph's root and the leaf. Because a leaf's graphics context +relies only on a linear path between the root and that node, the Java +3D renderer can decide to traverse the scene graph in whatever order it +wishes. It can traverse the scene graph from left to right and top to +bottom, in level order from right to left, or even in parallel. The +only exceptions to this rule are spatially bounded attributes such as +lights and fog. +

+

This characteristic is in marked contrast to many older scene +graph-based APIs (including PHIGS and SGI's Inventor) where, if a node +above or to the left of a node changes the graphics state, the change +affects the graphics state of all nodes below it or to its right.

+

The most common node object, along the path from the root to the +leaf, +that changes the graphics state is the TransformGroup object. The +TransformGroup object can change the position, orientation, and scale +of the objects below it.

+

Most graphics state attributes are set by a Shape3D leaf node +through +its constituent Appearance object, thus allowing parallel rendering. +The Shape3D node also has a constituent Geometry object that specifies +its geometry-this permits different shape objects to share common +geometry without sharing material attributes (or vice versa).

+

+

Rendering

+

The Java 3D renderer incorporates all graphics state changes made in +a +direct path from a scene graph root to a leaf object in the drawing of +that leaf object. Java 3D provides this semantic for both retained and +compiled-retained modes. +

+

+

Scene Graph Objects

+

A Java 3D scene graph consists of a collection of Java 3D node +objects +connected in a tree structure. These node objects reference other scene +graph objects called node component objects. +All scene graph node and component objects are subclasses of a common +SceneGraphObject class. The +SceneGraphObject class is an abstract class +that defines methods that are common among nodes and component objects. +

+

Scene graph objects are constructed by creating a new instance of +the +desired class and are accessed and manipulated using the object's set +and get +methods. Once a scene graph object is created and connected to other +scene graph objects to form a subgraph, the entire subgraph can be +attached to a virtual universe---via a high-resolution Locale +object-making the object live. Prior to attaching a subgraph +to a virtual +universe, the entire subgraph can be compiled into an +optimized, internal format (see the +BranchGroup.compile() +method).

+

An important characteristic of all scene graph objects is that +they can +be accessed or modified only during the creation of a scene graph, +except where explicitly allowed. Access to most set and get +methods of objects that are part of a live or compiled scene graph is +restricted. Such restrictions provide the scene graph compiler with +usage information it can use in optimally compiling or rendering a +scene graph. Each object has a set of capability bits that enable +certain functionality when the object is live or compiled. By default, +all capability bits are disabled (cleared). Only those set +and get +methods corresponding to capability bits that are explicitly enabled +(set) prior to the object being compiled or made live are legal.
+

+

+

Scene Graph Superstructure +Objects

+Java 3D defines two scene graph superstructure objects, +VirtualUniverse +and Locale, which are used to contain +collections of subgraphs that +comprise the scene graph. These objects are described in more detail in +"Scene Graph Superstructure." +

+

VirtualUniverse Object

+A VirtualUniverse object +consists of a list of Locale objects that +contain a collection of scene graph nodes that exist in the universe. +Typically, an application will need only one VirtualUniverse, even for +very large virtual databases. Operations on a VirtualUniverse include +enumerating the Locale objects contained within the universe. +

+

Locale Object

+The Locale object acts as a container for +a collection of subgraphs of +the scene graph that are rooted by a BranchGroup node. A Locale also +defines a location within the virtual universe using high-resolution +coordinates (HiResCoord) to specify its position. The HiResCoord serves +as the origin for all scene graph objects contained within the Locale. +

A Locale has no parent in the scene graph but is implicitly +attached to +a virtual universe when it is constructed. A Locale may reference an +arbitrary number of BranchGroup nodes but has no explicit children.

+

The coordinates of all scene graph objects are relative to the +HiResCoord of the Locale in which they are contained. Operations on a +Locale include setting or getting the HiResCoord of the Locale, adding +a subgraph, and removing a subgraph.

+

+

Scene Graph Viewing Objects

+Java 3D defines five scene graph viewing objects that are not part of +the scene graph per se but serve to define the viewing parameters and +to provide hooks into the physical world. These objects are Canvas3D, +Screen3D, View, +PhysicalBody, and PhysicalEnvironment. They are +described in more detail in the "View Model" +document.
+

+

Canvas3D Object

+The Canvas3D object encapsulates all of +the parameters associated with +the window being rendered into. +When a Canvas3D object is attached to a View object, the Java 3D +traverser renders the specified view onto the canvas. Multiple Canvas3D +objects can point to the same View object. +

+

Screen3D Object

+The Screen3D object encapsulates all of +the +parameters associated with the physical screen containing the canvas, +such as the width and height of the screen in pixels, the physical +dimensions of the screen, and various physical calibration values. +

+

View Object

+The View object specifies information +needed to render the scene graph. +Figure +2 shows a View object attached to a simple scene graph for +viewing the scene. +

The View object is the central Java 3D object for coordinating all +aspects of viewing. +All viewing parameters in Java 3D are directly contained either within +the View object or within objects pointed to by a View object. Java 3D +supports multiple simultaneously active View objects, each of which can +render to one or more canvases.

+

+

PhysicalBody Object

+The PhysicalBody object encapsulates all of the +parameters associated with the physical body, such as head position, +right and left eye position, and so forth. +

+

PhysicalEnvironment Object

+

The PhysicalEnvironment object encapsulates all of the parameters +associated with the physical environment, such as calibration +information for the tracker base for the head or hand tracker.
+

+


+

+

Viewing a Scene Graph +

+

+
    + Figure 2 – Viewing a Scene Graph +
+ + diff --git a/src/javax/media/j3d/doc-files/SceneGraphSharing.html b/src/javax/media/j3d/doc-files/SceneGraphSharing.html new file mode 100644 index 0000000..ff80cb4 --- /dev/null +++ b/src/javax/media/j3d/doc-files/SceneGraphSharing.html @@ -0,0 +1,250 @@ + + + + + Java 3D API - Reusing Scene Graphs + + +

Reusing Scene Graphs

+

+Java 3D provides application programmers +with two different means for reusing scene graphs. First, multiple +scene graphs can share a common subgraph. Second, the node hierarchy of +a common subgraph can be cloned, while still sharing large component +objects such as geometry and texture objects. In the first case, +changes in the shared subgraph affect all scene graphs that refer to +the shared subgraph. In the second case, each instance is unique-a +change in one instance does not affect any other instance. +

+

Sharing Subgraphs

+

An application that wishes to share a subgraph from multiple places +in +a scene graph must do so through the use of the Link +leaf node and an +associated SharedGroup node. The +SharedGroup node serves as the root of +the shared subgraph. The Link leaf node refers to the SharedGroup node. +It does not incorporate the shared scene graph directly into its scene +graph. +

+

A SharedGroup node allows multiple Link leaf nodes to share its +subgraph as shown in Figure +1 below.
+

+

Sharing a Subgraph +

+
    + Figure 1 – Sharing a Subgraph +
+

Cloning Subgraphs

+

An application developer may wish to reuse a common subgraph without +completely sharing that subgraph. For example, the developer may wish +to create a parking lot scene consisting of multiple cars, each with a +different color. The developer might define three basic types of cars, +such as convertible, truck, and sedan. To create the parking lot scene, +the application will instantiate each type of car several times. Then +the application can change the color of the various instances to create +more variety in the scene. Unlike shared subgraphs, each instance is a +separate copy of the scene graph definition: Changes to one instance do +not affect any other instance. +

+

Java 3D provides the cloneTree +method for this +purpose. The cloneTree +method allows the programmer to change some attributes (NodeComponent +objects) in a scene graph, while at the same time sharing the majority +of the scene graph data-the geometry. +

+

References to Node Component +Objects

+

When cloneTree reaches a leaf node, +there are two possible actions for handling the leaf node's +NodeComponent objects (such as Material, Texture, and so forth). First, +the cloned leaf node can reference the original leaf node's +NodeComponent object-the NodeComponent object itself is not duplicated. +Since the cloned leaf node shares the NodeComponent object with the +original leaf node, changing the data in the NodeComponent object will +effect a change in both nodes. This mode would also be used for objects +that are read-only at run time. +

+

Alternatively, the NodeComponent object can be duplicated, in which +case the new leaf node would reference the duplicated object. This mode +allows data referenced by the newly created leaf node to be modified +without that modification affecting the original leaf node. +

+

Figure +2 +shows two instances of NodeComponent objects that are shared and one +NodeComponent element that is duplicated for the cloned subgraph. +

+

Referenced and Duplicated NodeComponent Objects +

+

+

+
    + Figure 2 – Referenced and Duplicated +NodeComponent Objects +
+

References to Other Scene +Graph Nodes

+Leaf nodes that contain references to other nodes +(for example, Light nodes reference a Group node) can create a problem +for the cloneTree method. After the cloneTree +operation is performed, the reference in the cloned leaf node will +still refer to the node in the original subgraph-a situation that is +most likely incorrect (see Figure +3). +

To handle these ambiguities, a callback mechanism is provided. +

+

References to Other Scene Graph Nodes +

+
    + Figure 3 – References to Other Scene Graph +Nodes +
+

+A leaf node that needs to update referenced nodes upon being duplicated +by a call to cloneTree must implement the updateNodeReferences +method. By using this method, the cloned leaf node can determine if any +nodes referenced by it have been duplicated and, if so, update the +appropriate references to their cloned counterparts. +

+

Suppose, for instance, that the leaf node Lf1 in Figure +3 implemented the updateNodeReferences method. Once +all nodes had been duplicated, the clone-Tree method +would then call each cloned leaf's node updateNodeReferences +method. When cloned leaf node Lf2's method was called, Lf2 could ask if +the node N1 had been duplicated during the cloneTree +operation. If the node had been duplicated, leaf Lf2 could then update +its internal state with the cloned node, N2 (see Figure +4). +

+

Updated Subgraph after updateNodeReferences Call +

+

+

+
    + Figure 4 – Updated Subgraph after +updateNodeReferences Call +
+

+All predefined Java 3D nodes will automatically have their updateNodeReferences +method defined. Only subclassed nodes that reference other nodes need +to have this method overridden by the user. +

+

Dangling References

+Because cloneTree is able to start +the cloning operation from any node, there is a potential for creating +dangling references. A dangling reference can occur only when a leaf +node that contains a reference to another scene graph node is cloned. +If the referenced node is not cloned, a dangling reference situation +exists: There are now two leaf nodes that access the same node (Figure +5). A dangling reference is discovered when a leaf node's updateNodeReferences +method calls the getNewNodeReference method and the +cloned subgraph does not contain a counterpart to the node being looked +up. +

Dangling Reference

+

+

+
    + Figure 5 – Dangling Reference: Bold Nodes +Are Being Cloned +
+

+When a dangling reference is discovered, cloneTree can +handle it in one of two ways. If cloneTree is called +without the allowDanglingReferences parameter set to true, +a dangling reference will result in a DanglingReferenceException +being thrown. The user can catch this exception if desired. If cloneTree +is called with the allowDanglingReferences parameter set +to true, the update-NodeReferences method +will return a reference to the same object passed into the getNewNodeReference +method. This will result in the cloneTree operation +completing with dangling references, as in Figure +5. +

+

Subclassing Nodes

+All Java 3D predefined nodes (for example, Interpolators and LOD +nodes) +automatically handle all node reference and duplication operations. +When a user subclasses a Leaf object or a NodeComponent object, certain +methods must be provided in order to ensure the proper operation of cloneTree. +

Leaf node subclasses (for example, Behaviors) that contain any user +node-specific data that needs to be duplicated during a cloneTree +operation must define the following two methods: +

+
Node cloneNode(boolean forceDuplicate);
void duplicateNode(Node n, boolean forceDuplicate)
+The cloneNode method consists of three lines: +


UserSubClass usc = new UserSubClass();
usc.duplicateNode(this, forceDuplicate);

return usc;


+The duplicateNode method must first call super.duplicateNode +before duplicating any necessary user-specific data or setting any +user-specific state. +

NodeComponent subclasses that contain any user node-specific data +must define the following two methods: +

+
NodeComponent cloneNodeComponent();
void duplicateNodeComponent(NodeComponent nc, boolean forceDuplicate);
+The cloneNodeComponent method consists of three lines: +


UserNodeComponent unc = new UserNodeComponent();
unc.duplicateNodeComponent(this, forceDuplicate);

return un;


+The duplicateNodeComponent must first call super.duplicateNodeComponent +and then can duplicate any user-specific data or set any user-specific +state as necessary. +

NodeReferenceTable Object

+The NodeReferenceTable object is used by a leaf node's updateNodeReferences +method called by the cloneTree +operation. The NodeReferenceTable maps nodes from the original subgraph +to the new nodes in the cloned subgraph. This information can than be +used to update any cloned leaf node references to reference nodes in +the cloned subgraph. This object can be created only by Java 3D. +

Example: User Behavior Node

+The following is an example of a user-defined Behavior object to show +properly how to define a node to be compatible with the cloneTree +operation. +
+
class RotationBehavior extends Behavior {
TransformGroup objectTransform;
WakeupOnElapsedFrames w;
+
    Matrix4d rotMat = new Matrix4d();
Matrix4d objectMat = new Matrix4d();
Transform3D t = new Transform3D();
+
    // Override Behavior's initialize method to set up wakeup
// criteria
+
    public void initialize() {
+
        // Establish initial wakeup criteria
+
        wakeupOn(w);
}
+
    // Override Behavior's stimulus method to handle the event
+
    public void processStimulus(Enumeration criteria) {
+
        // Rotate by another PI/120.0 radians
+
        objectMat.mul(objectMat, rotMat);
t.set(objectMat);
objectTransform.setTransform(t);
+
        // Set wakeup criteria for next time
+
        wakeupOn(w);
}
+
    // Constructor for rotation behavior.
+
    public RotationBehavior(TransformGroup tg, int numFrames) {
w = new WakeupOnElapsedFrames(numFrames);
objectTransform = tg;
+
        objectMat.setIdentity();
+
        // Create a rotation matrix that rotates PI/120.0
// radians per frame
rotMat.rotX(Math.PI/120.0);
+
        // Note: When this object is duplicated via cloneTree,
// the cloned RotationBehavior node needs to point to
// the TransformGroup in the just-cloned tree.
}
+
    // Sets a new TransformGroup.
+
    public void setTransformGroup(TransformGroup tg) {
objectTransform = tg;
+
    }
+
    // The next two methods are needed for cloneTree to operate
// correctly.
// cloneNode is needed to provide a new instance of the user
// derived subclass.
+
    public Node cloneNode(boolean forceDuplicate) {
+
        // Get all data from current node needed for
// the constructor
int numFrames = w.getElapsedFrameCount();
+
        RotationBehavior r =
new RotationBehavior(objectTransform, numFrames);
r.duplicateNode(this, forceDuplicate);
return r;
}
+
    // duplicateNode is needed to duplicate all super class
// data as well as all user data.
+
    public void duplicateNode(Node originalNode, boolean 
forceDuplicate) {
super.duplicateNode(originalNode, forceDuplicate);
+
        // Nothing to do here - all unique data was handled
// in the constructor in the cloneNode routine.
}
+
    // Callback for when this leaf is cloned. For this object
// we want to find the cloned TransformGroup node that this
// clone Leaf node should reference.
+
    public void updateNodeReferences(NodeReferenceTable t) {
+
        super.updateNodeReferences(t);
+
        // Update node's TransformGroup to proper reference
+
        TransformGroup newTg =
(TransformGroup)t.getNewObjectReference(
objectTransform);
setTransformGroup(newTg);
}
}
+ + diff --git a/src/javax/media/j3d/doc-files/SceneGraphSharing1.gif b/src/javax/media/j3d/doc-files/SceneGraphSharing1.gif new file mode 100644 index 0000000..f6ca47c Binary files /dev/null and b/src/javax/media/j3d/doc-files/SceneGraphSharing1.gif differ diff --git a/src/javax/media/j3d/doc-files/SceneGraphSharing2.gif b/src/javax/media/j3d/doc-files/SceneGraphSharing2.gif new file mode 100644 index 0000000..c062c81 Binary files /dev/null and b/src/javax/media/j3d/doc-files/SceneGraphSharing2.gif differ diff --git a/src/javax/media/j3d/doc-files/SceneGraphSharing3.gif b/src/javax/media/j3d/doc-files/SceneGraphSharing3.gif new file mode 100644 index 0000000..325cab1 Binary files /dev/null and b/src/javax/media/j3d/doc-files/SceneGraphSharing3.gif differ diff --git a/src/javax/media/j3d/doc-files/SceneGraphSharing4.gif b/src/javax/media/j3d/doc-files/SceneGraphSharing4.gif new file mode 100644 index 0000000..78aeaab Binary files /dev/null and b/src/javax/media/j3d/doc-files/SceneGraphSharing4.gif differ diff --git a/src/javax/media/j3d/doc-files/SceneGraphSharing5.gif b/src/javax/media/j3d/doc-files/SceneGraphSharing5.gif new file mode 100644 index 0000000..2ff6547 Binary files /dev/null and b/src/javax/media/j3d/doc-files/SceneGraphSharing5.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewBranch.gif b/src/javax/media/j3d/doc-files/ViewBranch.gif new file mode 100644 index 0000000..75cc40d Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewBranch.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel.html b/src/javax/media/j3d/doc-files/ViewModel.html new file mode 100644 index 0000000..3cc9ece --- /dev/null +++ b/src/javax/media/j3d/doc-files/ViewModel.html @@ -0,0 +1,1064 @@ + + + + + Java 3D API - View Model + + +

View Model

+

Java 3D introduces a new view model that takes Java's +vision of "write once, run anywhere" and generalizes it to include +display devices and six-degrees-of-freedom input peripherals such as +head trackers. This "write once, view everywhere" nature of the new +view model means that an application or applet written using the Java +3D view model can render images to a broad range of display devices, +including standard computer displays, multiple-projection display +rooms, and head-mounted displays, without modification of the scene +graph. It also means that the same application, once again without +modification, can render stereoscopic views and can take advantage of +the input from a head tracker to control the rendered view. +

+

Java 3D's view model achieves this versatility by cleanly +separating +the virtual and the physical world. This model distinguishes between +how an application positions, orients, and scales a ViewPlatform object +(a viewpoint) within the virtual world and how the Java 3D +renderer +constructs the final view from that viewpoint's position and +orientation. The application controls the ViewPlatform's position and +orientation; the renderer computes what view to render using this +position and orientation, a description of the end-user's physical +environment, and the user's position and orientation within the +physical environment. +

+

This document first explains why Java 3D chose a different view +model +and some of the philosophy behind that choice. It next describes how +that model operates in the simple case of a standard computer screen +without head tracking—the most common case. Finally, it presents +advanced material that was originally published in Appendix C of the +API specification guide. +

+

+

+

Why a New Model?

+

Camera-based view models, as found in low-level APIs, give +developers +control over all rendering parameters. This makes sense when dealing +with custom applications, less sense when dealing with systems that +wish to have broader applicability: systems such as viewers or browsers +that load and display whole worlds as a single unit or systems where +the end users view, navigate, display, and even interact with the +virtual world. +

+

Camera-based view models emulate a camera in the virtual world, not +a +human in a virtual world. Developers must continuously reposition a +camera to emulate "a human in the virtual world." +

+

The Java 3D view model incorporates head tracking directly, if +present, +with no additional effort from the developer, thus providing end users +with the illusion that they actually exist inside a virtual world. +

+

The Java 3D view model, when operating in a non-head-tracked +environment and rendering to a single, standard display, acts very much +like a traditional camera-based view model, with the added +functionality of being able to generate stereo views transparently. +

+

+

+

The Physical Environment +Influences the View

+

Letting the application control all viewing parameters is not +reasonable in systems in which the physical environment dictates some +of the view parameters. +

+

One example of this is a head-mounted display (HMD), where the +optics +of the head-mounted display directly determine the field of view that +the application should use. Different HMDs have different optics, +making it unreasonable for application developers to hard-wire such +parameters or to allow end users to vary that parameter at will. +

+

Another example is a system that automatically computes view +parameters +as a function of the user's current head position. The specification of +a world and a predefined flight path through that world may not exactly +specify an end-user's view. HMD users would expect to look and thus see +to their left or right even when following a fixed path through the +environment-imagine an amusement park ride with vehicles that follow +fixed paths to present content to their visitors, but visitors can +continue to move their heads while on those rides. +

+

Depending on the physical details of the end-user's environment, the +values of the viewing parameters, particularly the viewing and +projection matrices, will vary widely. The factors that influence the +viewing and projection matrices include the size of the physical +display, how the display is mounted (on the user's head or on a table), +whether the computer knows the user's head location in three space, the +head mount's actual field of view, the display's pixels per inch, and +other such parameters. For more information, see "View Model Details." +

+

+

+

Separation of Physical and +Virtual

+

The Java 3D view model separates the virtual environment, where +the +application programmer has placed objects in relation to one another, +from the physical environment, where the user exists, sees computer +displays, and manipulates input devices. +

+

Java 3D also defines a fundamental correspondence between the +user's +physical world and the virtual world of the graphic application. This +physical-to-virtual-world correspondence defines a single common space, +a space where an action taken by an end user affects objects within the +virtual world and where any activity by objects in the virtual world +affects the end user's view. +

+

+

+

The Virtual World

+

The virtual world is a common space in which virtual objects exist. +The +virtual world coordinate system exists relative to a high-resolution +Locale-each Locale object defines the origin of virtual world +coordinates for all of the objects attached to that Locale. The Locale +that contains the currently active ViewPlatform object defines the +virtual world coordinates that are used for rendering. Java3D +eventually transforms all coordinates associated with scene graph +elements into this common virtual world space. +

+

The Physical World

+

The physical world is just that-the real, physical world. This is +the +space in which the physical user exists and within which he or she +moves his or her head and hands. This is the space in which any +physical trackers define their local coordinates and in which several +calibration coordinate systems are described. +

+

The physical world is a space, not a common coordinate system +between +different execution instances of Java 3D. So while two different +computers at two different physical locations on the globe may be +running at the same time, there is no mechanism directly within +Java 3D +to relate their local physical world coordinate systems with each +other. Because of calibration issues, the local tracker (if any) +defines the local physical world coordinate system known to a +particular instance of Java 3D. +

+

+

+

The Objects That Define the +View

+

Java 3D distributes its view model parameters across several +objects, +specifically, the View object and its associated component objects, the +PhysicalBody object, the PhysicalEnvironment object, the Canvas3D +object, and the Screen3D object. Figure +1 shows graphically the central role of the View object and the +subsidiary role of its component objects. +

+

View Object + Other Components

+

+

+
    + Figure 1 – View Object, Its Component +Objects, and Their +Interconnection +
+

+The view-related objects shown in Figure +1 +and their roles are as follows. For each of these objects, the portion +of the API that relates to modifying the virtual world and the portion +of the API that is relevant to non-head-tracked standard display +configurations are derived in this chapter. The remainder of the +details are described in "View Model +Details." +

+ +
    +
  • View: The main view object. +It contains many pieces of +view state.
  • +
+
    +
  • Canvas3D: The 3D version +of the Abstract Windowing +Toolkit +(AWT) Canvas object. It represents a window in which Java 3D will +draw +images. It contains a reference to a Screen3D object and information +describing the Canvas3D's size, shape, and location within the Screen3D +object.
  • +
+
    +
  • Screen3D: An object that +contains information describing +the display screen's physical properties. Java 3D places +display-screen +information in a separate object to prevent the duplication of screen +information within every Canvas3D object that shares a common screen.
  • +
+
    +
  • PhysicalBody: An object that +contains calibration information +describing the user's physical body.
  • +
+
    +
  • PhysicalEnvironment: An +object that contains calibration +information describing the physical world, mainly information that +describes the environment's six-degrees-of freedom tracking hardware, +if present.
  • +
+

Together, these objects describe the geometry of viewing rather than +explicitly providing a viewing or projection matrix. The Java 3D +renderer uses this information to construct the appropriate viewing and +projection matrices. The geometric focus of these view objects provides +more flexibility in generating views-a flexibility needed to support +alternative display configurations. +

+

ViewPlatform: A Place in the +Virtual World

+

A ViewPlatform leaf node defines a coordinate system, and thus a +reference frame with its associated origin or reference point, within +the virtual world. The ViewPlatform serves as a point of attachment for +View objects and as a base for determining a renderer's view. +

+

Figure +2 +shows a portion of a scene graph containing a ViewPlatform node. The +nodes directly above a ViewPlatform determine where that ViewPlatform +is located and how it is oriented within the virtual world. By +modifying the Transform3D object associated with a TransformGroup node +anywhere directly above a ViewPlatform, an application or behavior can +move that ViewPlatform anywhere within the virtual world. A simple +application might define one TransformGroup node directly above a +ViewPlatform, as shown in Figure +2. +

+

A VirtualUniverse may have many different ViewPlatforms, but a +particular View object can attach itself only to a single ViewPlatform. +Thus, each rendering onto a Canvas3D is done from the point of view of +a single ViewPlatform. +

+

View Platform Branch Graph +

+

+

+
    + Figure 2 – A Portion of a Scene Graph +Containing a ViewPlatform Object +
+

+

+

Moving through the Virtual +World

+

An application navigates within the virtual world by modifying a +ViewPlatform's parent TransformGroup. Examples of applications that +modify a ViewPlatform's location and orientation include browsers, +object viewers that provide navigational controls, applications that do +architectural walkthroughs, and even search-and-destroy games. +

+

Controlling the ViewPlatform object can produce very interesting and +useful results. Our first simple scene graph (see "Introduction," Figure 1) +defines a scene graph for a simple application that draws an object in +the center of a window and rotates that object about its center point. +In that figure, the Behavior object modifies the TransformGroup +directly above the Shape3D node. +

+

An alternative application scene graph, shown in Figure +3, +leaves the central object alone and moves the ViewPlatform around the +world. If the shape node contains a model of the earth, this +application could generate a view similar to that seen by astronauts as +they orbit the earth. +

+

Had we populated this world with more objects, this scene graph +would allow navigation through the world via the Behavior node. +

+

Simple Scene Graph with View Control +

+

+

+
    + Figure 3 – A Simple Scene Graph with View +Control +
+

+Applications and behaviors manipulate a TransformGroup through its +access methods. These methods allow an application to retrieve and +set the Group node's Transform3D object. Transform3D Node methods +include getTransform and setTransform. +

+

+

+

Dropping in on a Favorite +Place

+

A scene graph may contain multiple ViewPlatform +objects. If a user detaches a View object +from a ViewPlatform and then +reattaches that View to a different ViewPlatform, the image on the +display will now be rendered from the point of view of the new +ViewPlatform.

+

Associating Geometry with a +ViewPlatform

+

Java 3D does not have any built-in semantics for displaying a +visible +manifestation of a ViewPlatform within the virtual world (an avatar). +However, a developer can construct and manipulate an avatar using +standard Java 3D constructs. +

+

A developer can construct a small scene graph consisting of a +TransformGroup node, a behavior leaf node, and a shape node and insert +it directly under the BranchGroup node associated with the ViewPlatform +object. The shape node would contain a geometric model of the avatar's +head. The behavior node would change the TransformGroup's transform +periodically to the value stored in a View object's UserHeadToVworld +parameter (see "View Model +Details"). +The avatar's virtual head, represented by the shape node, will now move +around in lock-step with the ViewPlatform's TransformGroup and any +relative position and orientation changes of the user's actual physical +head (if a system has a head tracker). +

+

+

+

Generating a View

+

Java 3D generates viewing matrices in one of a few different +ways, +depending on whether the end user has a head-mounted or a room-mounted +display environment and whether head tracking is enabled. This section +describes the computation for a non-head-tracked, room-mounted +display-a standard computer display. Other environments are described +in "View Model Details." +

+

In the absence of head tracking, the ViewPlatform's origin specifies +the virtual eye's location and orientation within the virtual world. +However, the eye location provides only part of the information needed +to render an image. The renderer also needs a projection matrix. In the +default mode, Java 3D uses the projection policy, the specified +field-of-view information, and the front and back clipping distances to +construct a viewing frustum. +

+

+

+

Composing Model and Viewing +Transformations

+

Figure +4 +shows a simple scene graph. To draw the object labeled "S," +Java 3D +internally constructs the appropriate model, view platform, eye, and +projection matrices. Conceptually, the model transformation for a +particular object is computed by concatenating all the matrices in a +direct path between the object and the VirtualUniverse. The view matrix +is then computed-again, conceptually-by concatenating all the matrices +between the VirtualUniverse object and the ViewPlatform attached to the +current View object. The eye and projection matrices are constructed +from the View object and its associated component objects. +

+

Object and ViewPlatform Transform

+

+

+
    + Figure 4 – Object and ViewPlatform +Transformations +
+

In our scene graph, what we would normally consider the +model transformation would consist of the following three +transformations: LT1T2. By +multiplying LT1T2 +by a vertex in the shape object, we would transform that vertex into +the virtual universe's coordinate system. What we would normally +consider the view platform transformation would be (LTv1)-1 +or Tv1-1L-1. +This presents a problem since coordinates in the virtual universe are +256-bit fixed-point values, which cannot be used to represent +transformed points efficiently. +

+

Fortunately, however, there is a solution to this problem. Composing +the model and view platform transformations gives us +

+
+

+
+
Tv1-1L-1LT1T2 += Tv1-1IT1T2 += Tv1-1T1T2, +
+
+

the matrix that takes vertices in an object's local coordinate +system +and places them in the ViewPlatform's coordinate system. Note that the +high-resolution Locale transformations cancel each other out, which +removes the need to actually transform points into high-resolution +VirtualUniverse coordinates. The general formula of the matrix that +transforms object coordinates to ViewPlatform coordinates is Tvn-1...Tv2-1Tv1-1T1T2...Tm. +

+

As mentioned earlier, the View object contains the remainder of the +view information, specifically, the eye matrix, E, +that takes points in the View-Platform's local coordinate system and +translates them into the user's eye coordinate system, and the +projection matrix, P, that projects objects in the +eye's coordinate system into clipping coordinates. The final +concatenation of matrices for rendering our shape object "S" on the +specified Canvas3D is PETv1-1T1T2. +In general this is PETvn-1...Tv2-1Tv1-1T1T2...Tm. +

+

The details of how Java 3D constructs the matrices E +and P in different end-user configurations are +described in "View Model Details." +

+

+

+

Multiple Locales

+

Java 3D supports multiple high-resolution Locales. In some +cases, +these +Locales are close enough to each other that they can "see" each other, +meaning that objects can be rendered even though they are not in the +same Locale as the ViewPlatform object that is attached to the View. +Java 3D automatically handles this case without the application +having +to do anything. As in the previous example, where the ViewPlatform and +the object being rendered are attached to the same Locale, Java 3D +internally constructs the appropriate matrices for cases in which the +ViewPlatform and the object being rendered are not attached +to the same Locale. +

+

Let's take two Locales, L1 and L2, with the View attached to a +ViewPlatform in L1. According to our general formula, the modeling +transformation-the transformation that takes points in object +coordinates and transforms them into VirtualUniverse coordinates-is LT1T2...Tm. +In our specific example, a point in Locale L2 would be transformed into +VirtualUniverse coordinates by L2T1T2...Tm. +The view platform transformation would be (L1Tv1Tv1...Tvn)-1 +or Tvn-1...Tv2-1Tv1-1L1-1. +Composing these two matrices gives us +

+
+

+
+
Tvn-1...Tv2-1Tv1-1L1-1L2T1T2...Tm. +
+
+

Thus, to render objects in another Locale, it is sufficient to +compute L1-1L2 +and use that as the starting matrix when composing the model +transformations. Given that a Locale is represented by a single +high-resolution coordinate position, the transformation L1-1L2 +is a simple translation by L2 - L1. +Again, it is not actually necessary to transform points into +high-resolution VirtualUniverse coordinates. +

+

In general, Locales that are close enough that the difference in +their +high-resolution coordinates can be represented in double precision by a +noninfinite value are close enough to be rendered. In practice, more +sophisticated culling techniques can be used to render only those +Locales that really are "close enough." +

+

+

+

A Minimal Environment

+

An application must create a minimal set of Java 3D objects +before +Java +3D can render to a display device. 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
  • +
+
    +
  • A ViewPlatform leaf node object that defines the position and +orientation within the virtual universe for generating views
  • +
+
+

View Model Details

+

An application programmer writing a 3D +graphics program that will deploy on a variety of platforms must +anticipate the likely end-user environments and must carefully +construct the view transformations to match those characteristics using +a low-level API. This appendix addresses many of the issues an +application must face and describes the sophisticated features that +Java 3D's advanced view model provides. +

+

+

+

An Overview of the +Java 3D +View Model

+Both camera-based and Java 3D-based view models allow a programmer +to +specify the shape of a view frustum and, under program control, to +place, move, and reorient that frustum within the virtual environment. +However, how they do this varies enormously. Unlike the camera-based +system, the Java 3D view model allows slaving the view frustum's +position and orientation to that of a six-degrees-of-freedom tracking +device. By slaving the frustum to the tracker, Java 3D can +automatically modify the view frustum so that the generated images +match the end-user's viewpoint exactly. +

Java 3D must handle two rather different head-tracking +situations. +In one case, we rigidly attach a tracker's base, +and thus its coordinate frame, to the display environment. This +corresponds to placing a tracker base in a fixed position and +orientation relative to a projection screen within a room, to a +computer display on a desk, or to the walls of a multiple-wall +projection display. In the second head-tracking situation, we rigidly +attach a tracker's sensor, not its base, to the display +device. This corresponds to rigidly attaching one of that tracker's +sensors to a head-mounted display and placing the tracker base +somewhere within the physical environment. +

+

+

+

Physical Environments and +Their Effects

+Imagine an application where the end user sits on a magic carpet. The +application flies the user through the virtual environment by +controlling the carpet's location and orientation within the virtual +world. At first glance, it might seem that the application also +controls what the end user will see-and it does, but only +superficially. +

The following two examples show how end-user environments can +significantly affect how an application must construct viewing +transformations. +

+

+

+

A Head-Mounted Example

+Imagine that the end user sees the magic carpet and the virtual world +with a head-mounted display and head tracker. As the application flies +the carpet through the virtual world, the user may turn to look to the +left, to the right, or even toward the rear of the carpet. Because the +head tracker keeps the renderer informed of the user's gaze direction, +it might not need to draw the scene directly in front of the magic +carpet. The view that the renderer draws on the head-mount's display +must match what the end user would see if the experience had occurred +in the real world. +

A Room-Mounted Example

+Imagine a slightly different scenario where the end user sits in a +darkened room in front of a large projection screen. The application +still controls the carpet's flight path; however, the position and +orientation of the user's head barely influences the image drawn on the +projection screen. If a user looks left or right, then he or she sees +only the darkened room. The screen does not move. It's as if the screen +represents the magic carpet's "front window" and the darkened room +represents the "dark interior" of the carpet. +

By adding a left and right screen, we give the magic carpet rider a +more complete view of the virtual world surrounding the carpet. Now our +end user sees the view to the left or right of the magic carpet by +turning left or right. +

+

+

+

Impact of Head Position and +Orientation on the Camera

+In the head-mounted example, the user's head position and orientation +significantly affects a camera model's camera position and orientation +but hardly has any effect on the projection matrix. In the room-mounted +example, the user's head position and orientation contributes little to +a camera model's camera position and orientation; however, it does +affect the projection matrix. +

From a camera-based perspective, the application developer must +construct the camera's position and orientation by combining the +virtual-world component (the position and orientation of the magic +carpet) and the physical-world component (the user's instantaneous head +position and orientation). +

+

Java 3D's view model incorporates the appropriate abstractions +to +compensate automatically for such variability in end-user hardware +environments. +

+

+

+

The Coordinate Systems

+The basic view model consists of eight or nine coordinate systems, +depending on whether the end-user environment consists of a +room-mounted display or a head-mounted display. First, we define the +coordinate systems used in a room-mounted display environment. Next, we +define the added coordinate system introduced when using a head-mounted +display system. +

Room-Mounted Coordinate +Systems

+The room-mounted coordinate system is divided into the virtual +coordinate system and the physical coordinate system. Figure +5 +shows these coordinate systems graphically. The coordinate systems +within the grayed area exist in the virtual world; those outside exist +in the physical world. Note that the coexistence coordinate system +exists in both worlds. +

The Virtual Coordinate +Systems

+
The Virtual World Coordinate System
+The virtual world coordinate system encapsulates +the unified coordinate system for all scene graph objects in the +virtual environment. For a given View, the virtual world coordinate +system is defined by the Locale object that contains the ViewPlatform +object attached to the View. It is a right-handed coordinate system +with +x to the right, +y up, and +z toward +the viewer. +
The ViewPlatform Coordinate System
+The ViewPlatform coordinate system is the local coordinate system of +the ViewPlatform leaf node to which the View is attached. +

Display Rigidly Attached to Tracker Base

+

+

+
    + Figure 5 – Display Rigidly Attached to the +Tracker Base +
+

+

+
The Coexistence Coordinate System
+A primary implicit goal of any view model is to map a specified local +portion of the physical world onto a specified portion of the virtual +world. Once established, one can legitimately ask where the user's head +or hand is located within the virtual world or where a virtual object +is located in the local physical world. In this way the physical user +can interact with objects inhabiting the virtual world, and vice versa. +To establish this mapping, Java 3D defines a special coordinate +system, +called coexistence coordinates, that is defined to exist in both the +physical world and the virtual world. +

The coexistence coordinate system exists half in the virtual world +and +half in the physical world. The two transforms that go from the +coexistence coordinate system to the virtual world coordinate system +and back again contain all the information needed to expand or shrink +the virtual world relative to the physical world. It also contains the +information needed to position and orient the virtual world relative to +the physical world. +

+

Modifying the transform that maps the coexistence coordinate system +into the virtual world coordinate system changes what the end user can +see. The Java 3D application programmer moves the end user within +the +virtual world by modifying this transform. +

+

+

+

The Physical Coordinate +Systems

+
The Head Coordinate System
+The head coordinate system allows an application to import its user's +head geometry. The coordinate system provides a simple consistent +coordinate frame for specifying such factors as the location of the +eyes and ears. +
The Image Plate Coordinate System
+The image plate coordinate system corresponds with the physical +coordinate system of the image generator. The image plate is defined as +having its origin at the lower left-hand corner of the display area and +as lying in the display area's XY +plane. Note that image plate is a different coordinate system than +either left image plate or right image plate. These last two coordinate +systems are defined in head-mounted environments only. +
The Head Tracker Coordinate System
+The head tracker coordinate system corresponds to the +six-degrees-of-freedom tracker's sensor attached to the user's head. +The head tracker's coordinate system describes the user's instantaneous +head position. +
The Tracker Base Coordinate System
+The tracker base coordinate system corresponds to the emitter +associated with absolute position/orientation trackers. For those +trackers that generate relative position/orientation information, this +coordinate system is that tracker's initial position and orientation. +In general, this coordinate system is rigidly attached to the physical +world. +

Head-Mounted Coordinate +Systems

+Head-mounted coordinate systems divide the same virtual coordinate +systems and the physical coordinate systems. Figure +6 +shows these coordinate systems graphically. As with the room-mounted +coordinate systems, the coordinate systems within the grayed area exist +in the virtual world; those outside exist in the physical world. Once +again, the coexistence coordinate system exists in both worlds. The +arrangement of the coordinate system differs from those for a +room-mounted display environment. The head-mounted version of +Java 3D's +coordinate system differs in another way. It includes two image plate +coordinate systems, one for each of an end-user's eyes. +
The Left Image Plate and Right Image Plate Coordinate Systems
+The left image plate and right image plate +coordinate systems correspond with the physical coordinate system of +the image generator associated with the left and right eye, +respectively. The image plate is defined as having its origin at the +lower left-hand corner of the display area and lying in the display +area's XY plane. Note that the left image plate's XY +plane does not necessarily lie parallel to the right image plate's XY +plane. Note that the left image plate and the right image plate are +different coordinate systems than the room-mounted display +environment's image plate coordinate system. +

Display Rigidly Attached to Head Tracker

+

+

+
    + Figure 6 – Display Rigidly Attached to the +Head Tracker (Sensor) +
+

+

+

The Screen3D Object

+A Screen3D object represents one independent display device. The most +common environment for a Java 3D application is a desktop computer +with +or without a head tracker. Figure +7 shows a scene graph fragment for a display environment designed +for such an end-user environment. Figure +8 shows a display environment that matches the scene graph +fragment in Figure +7. +

Environment with Single Screen3D Object

+

+

+
    + Figure 7 – A Portion of a Scene Graph +Containing a Single Screen3D +Object +
+

+Single-Screen Display Environment

+

+

+
    + Figure 8 – A Single-Screen Display +Environment +
+

+A multiple-projection wall display presents a more exotic environment. +Such environments have multiple screens, typically three or more. Figure +9 shows a scene graph fragment representing such a system, and Figure +10 shows the corresponding display environment. +

+

Environment with Three Screen3D Object +

+

+

+
    + Figure 9 – A Portion of a Scene Graph +Containing Three Screen3D +Objects +
+

+Three-Screen Display Environment

+

+

+
    + Figure 10 – A Three-Screen Display +Environment +
+

+A multiple-screen environment requires more care during the +initialization and calibration phase. Java 3D must know how the +Screen3Ds are placed with respect to one another, the tracking device, +and the physical portion of the coexistence coordinate system. +

+

+

+

Viewing in Head-Tracked Environments

+

The "Generating a View" section +describes how Java 3D generates a view for a standard flat-screen +display with no head tracking. In this section, we describe how +Java 3D +generates a view in a room-mounted, head-tracked display +environment-either a computer monitor with shutter glasses and head +tracking or a multiple-wall display with head-tracked shutter glasses. +Finally, we describe how Java 3D generates view matrices in a +head-mounted and head-tracked display environment. +

+

A Room-Mounted Display with +Head Tracking

+When head tracking combines with a room-mounted +display environment (for example, a standard flat-screen display), the +ViewPlatform's origin and orientation serve as a base for constructing +the view matrices. Additionally, Java 3D uses the end-user's head +position and orientation to compute where an end-user's eyes are +located in physical space. Each eye's position serves to offset the +corresponding virtual eye's position relative to the ViewPlatform's +origin. Each eye's position also serves to specify that eye's frustum +since the eye's position relative to a Screen3D uniquely specifies that +eye's view frustum. Note that Java 3D will access the PhysicalBody +object to obtain information describing the user's interpupilary +distance and tracking hardware, values it needs to compute the +end-user's eye positions from the head position information. +

A Head-Mounted Display with +Head Tracking

+In a head-mounted environment, the ViewPlatform's origin and +orientation also serves as a base for constructing view matrices. And, +as in the head-tracked, room-mounted environment, Java 3D also +uses the +end-user's head position and orientation to modify the ViewPlatform's +position and orientation further. In a head-tracked, head-mounted +display environment, an end-user's eyes do not move relative to their +respective display screens, rather, the display screens move relative +to the virtual environment. A rotation of the head by an end user can +radically affect the final view's orientation. In this situation, Java +3D combines the position and orientation from the ViewPlatform with the +position and orientation from the head tracker to form the view matrix. +The view frustum, however, does not change since the user's eyes do not +move relative to their respective display screen, so Java 3D can +compute the projection matrix once and cache the result. +

If any of the parameters of a View object are updated, this will +effect +a change in the implicit viewing transform (and thus image) of any +Canvas3D that references that View object. +

+

+

+

Compatibility Mode

+

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. +

+

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 available only in compatibility mode. The setCompatibilityModeEnable +method turns compatibility mode on or off. Compatibility mode is +disabled by default. +

+
+

Note: 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. +

+
+

Overview of the +Camera-Based View Model

+The traditional camera-based view model, shown in Figure +11, +places a virtual camera inside a geometrically specified world. The +camera "captures" the view from its current location, orientation, and +perspective. The visualization system then draws that view on the +user's display device. The application controls the view by moving the +virtual camera to a new location, by changing its orientation, by +changing its field of view, or by controlling some other camera +parameter. +

The various parameters that users control in a +camera-based view model specify the shape of a viewing volume (known as +a frustum because of its truncated pyramidal shape) and locate that +frustum within the virtual environment. The rendering pipeline uses the +frustum to decide which objects to draw on the display screen. The +rendering pipeline does not draw objects outside the view frustum, and +it clips (partially draws) objects that intersect the frustum's +boundaries. +

+

Though a view frustum's specification may have many items in common +with those of a physical camera, such as placement, orientation, and +lens settings, some frustum parameters have no physical analog. Most +noticeably, a frustum has two parameters not found on a physical +camera: the near and far clipping planes. +

+

Camera-Based View Model +

+

+

+
    + Figure 11 – The Camera-Based View Model +
+

+The location of the near and far clipping planes allows the application +programmer to specify which objects Java 3D should not draw. +Objects +too far away from the current eyepoint usually do not result in +interesting images. Those too close to the eyepoint might obscure the +interesting objects. By carefully specifying near and far clipping +planes, an application programmer can control which objects the +renderer will not be drawing. +

+

From the perspective of the display device, the virtual camera's +image +plane corresponds to the display screen. The camera's placement, +orientation, and field of view determine the shape of the view frustum. +

+

+

+

Using the Camera-Based View +Model

+

The camera-based view model allows Java 3D to bridge the gap +between +existing 3D code and Java 3D's view model. By using the +camera-based +view model methods, a programmer retains the familiarity of the older +view model but gains some of the flexibility afforded by Java 3D's +new +view model. +

+

The traditional camera-based view model is supported in Java 3D +by +helping methods in the Transform3D object. These methods were +explicitly designed to resemble as closely as possible the view +functions of older packages and thus should be familiar to most 3D +programmers. The resulting Transform3D objects can be used to set +compatibility-mode transforms in the View object. +

+

+

+

Creating a Viewing Matrix

+

The Transform3D object provides a lookAt utility +method +to create a +viewing matrix. This method specifies the position and orientation of +a viewing transform. It works similarly to the equivalent function in +OpenGL. The inverse of this transform can be used to control the +ViewPlatform object within the scene graph. Alternatively, this +transform can be passed directly to the View's VpcToEc +transform via the compatibility-mode viewing functions. The setVpcToEc +method is used to set the viewing matrix when in compatibility mode. +

+

Creating a Projection +Matrix

+

The Transform3D object provides three methods for +creating a projection matrix: frustum, perspective, +and ortho. All three map points from eye coordinates +(EC) to clipping coordinates (CC). Eye coordinates are defined such +that (0, 0, 0) is at the eye and the projection plane is at z += -1.
+

+

The frustum method +establishes a perspective projection with the eye at the apex of a +symmetric view frustum. The transform maps points from eye coordinates +to clipping coordinates. The clipping coordinates generated by the +resulting transform are in a right-handed coordinate system (as are all +other coordinate systems in Java 3D). +

+

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. The -far +parameter specifies the far clipping plane. See Figure +12. +

+

The perspective method establishes a perspective +projection with the eye at the apex of a symmetric view frustum, +centered about the Z-axis, +with a fixed field of view. The resulting perspective projection +transform mimics a standard camera-based view model. The transform maps +points from eye coordinates to clipping coordinates. The clipping +coordinates generated by the resulting transform are in a right-handed +coordinate system. +

+

The arguments define the frustum and its associated perspective +projection: -near and -far specify the near +and far clipping planes; fovx specifies the field of view +in the X dimension, in radians; and aspect +specifies the aspect ratio of the window. See Figure +13. +

+

Perspective Viewing Frustum +

+

+

+
    + Figure 12 – A Perspective Viewing Frustum +
+

+Perspective View Model Arguments

+

+

+
    + Figure 13 – Perspective View Model Arguments +
+

+The ortho method +establishes a parallel projection. The orthographic projection +transform mimics a standard camera-based video model. The transform +maps points from eye coordinates to clipping coordinates. The clipping +coordinates generated by the resulting transform are in a right-handed +coordinate system. +

+

The arguments define a rectangular box used for 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. The -far +parameter specifies the far clipping plane. See Figure +14. +

+

Orthographic View Model +

+

+

+
    + Figure 14 – Orthographic View Model +
+

+

+

The setLeftProjection +and setRightProjection methods are used to set the +projection matrices for the left eye and right eye, respectively, when +in compatibility mode.

+ + diff --git a/src/javax/media/j3d/doc-files/ViewModel1.gif b/src/javax/media/j3d/doc-files/ViewModel1.gif new file mode 100644 index 0000000..e94743e Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel1.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel10.gif b/src/javax/media/j3d/doc-files/ViewModel10.gif new file mode 100644 index 0000000..aceb6e7 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel10.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel11.gif b/src/javax/media/j3d/doc-files/ViewModel11.gif new file mode 100644 index 0000000..f943c15 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel11.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel12.gif b/src/javax/media/j3d/doc-files/ViewModel12.gif new file mode 100644 index 0000000..787afe7 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel12.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel13.gif b/src/javax/media/j3d/doc-files/ViewModel13.gif new file mode 100644 index 0000000..a8482ef Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel13.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel14.gif b/src/javax/media/j3d/doc-files/ViewModel14.gif new file mode 100644 index 0000000..f201443 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel14.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel2.gif b/src/javax/media/j3d/doc-files/ViewModel2.gif new file mode 100644 index 0000000..2d549b1 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel2.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel3.gif b/src/javax/media/j3d/doc-files/ViewModel3.gif new file mode 100644 index 0000000..5285015 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel3.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel4.gif b/src/javax/media/j3d/doc-files/ViewModel4.gif new file mode 100644 index 0000000..ab9db1d Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel4.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel5.gif b/src/javax/media/j3d/doc-files/ViewModel5.gif new file mode 100644 index 0000000..859b456 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel5.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel6.gif b/src/javax/media/j3d/doc-files/ViewModel6.gif new file mode 100644 index 0000000..2200595 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel6.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel7.gif b/src/javax/media/j3d/doc-files/ViewModel7.gif new file mode 100644 index 0000000..ec84ac2 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel7.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel8.gif b/src/javax/media/j3d/doc-files/ViewModel8.gif new file mode 100644 index 0000000..ee4b331 Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel8.gif differ diff --git a/src/javax/media/j3d/doc-files/ViewModel9.gif b/src/javax/media/j3d/doc-files/ViewModel9.gif new file mode 100644 index 0000000..0cbf72c Binary files /dev/null and b/src/javax/media/j3d/doc-files/ViewModel9.gif differ diff --git a/src/javax/media/j3d/doc-files/VirtualUniverse.gif b/src/javax/media/j3d/doc-files/VirtualUniverse.gif new file mode 100644 index 0000000..4d713a8 Binary files /dev/null and b/src/javax/media/j3d/doc-files/VirtualUniverse.gif differ diff --git a/src/javax/media/j3d/doc-files/VirtualUniverse.html b/src/javax/media/j3d/doc-files/VirtualUniverse.html new file mode 100644 index 0000000..2f9dd14 --- /dev/null +++ b/src/javax/media/j3d/doc-files/VirtualUniverse.html @@ -0,0 +1,265 @@ + + + + + Java 3D API - Scene Graph Superstructure + + +

Scene Graph Superstructure

+

Java 3D's superstructure consists of one or more +VirtualUniverse objects, each of which contains a set of one or more +high-resolution Locale objects. The Locale objects, in turn, contain +collections of subgraphs that comprise the scene graph (see Figure +1). +

+

+

+

The Virtual Universe

+Java 3D defines the concept of a virtual universe +as a three-dimensional space with an associated set of objects. Virtual +universes serve as the largest unit of aggregate representation, and +can also be thought of as databases. Virtual universes can be very +large, both in physical space units and in content. Indeed, in most +cases a single virtual universe will serve an application's entire +needs. +

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. +

+

To support large virtual universes, Java 3D introduces the concept +of Locales that have high-resolution coordinates +as an origin. Think of high-resolution coordinates as "tie-downs" that +precisely anchor the locations of objects specified using less precise +floating-point coordinates that are within the range of influence of +the high-resolution coordinates. +

+

A Locale, with its associated high-resolution coordinates, serves as +the next level of representation down from a virtual universe. All +virtual universes contain one or more high-resolution-coordinate +Locales, and all other objects are attached to a Locale. +High-resolution coordinates act as an upper-level translation-only +transform node. For example, the coordinates of all objects that are +attached to a particular Locale are all relative to the location of +that Locale's high-resolution coordinates. +

+

The Virtual Universe +

+

+

+
    + Figure 1 – The Virtual Universe +
+

+While a virtual universe is similar to the traditional computer +graphics concept of a scene graph, a given virtual universe can become +so large that it is often better to think of a scene graph as the +descendant of a high-resolution-coordinate Locale. +

+

+

+

Establishing a Scene

+To construct a three-dimensional scene, the programmer must execute a +Java 3D program. The Java 3D application must first create a +VirtualUniverse object and attach at least one Locale to it. Then the +desired scene graph is constructed, starting with a BranchGroup node +and including at least one ViewPlatform object, and the scene graph is +attached to the Locale. Finally, a View object that references the +ViewPlatform object (see "Structuring +the Java 3D Program") +is constructed. As soon as a scene graph containing a ViewPlatform is +attached to the VirtualUniverse, Java 3D's rendering loop is engaged, +and the scene will appear on the drawing canvas(es) associated with the +View object. +

Loading a Virtual Universe

+Java 3D is a runtime application programming +interface (API), not a file format. As an API, Java 3D provides no +direct mechanism for loading or storing a virtual universe. +Constructing a scene graph involves the execution of a Java 3D program. +However, loaders to convert a number of standard 3D file formats to or +from Java 3D virtual universes are expected to be generally available. +

Coordinate Systems

+By default, Java 3D coordinate systems are right-handed, with the +orientation semantics being that +y is the local gravitational +up, +x is horizontal to the right, and +z is directly +toward the viewer. The default units are meters. +

High-Resolution Coordinates

+Double-precision floating-point, single-precision floating-point, or +even fixed-point representations of three-dimensional coordinates are +sufficient to represent and display rich 3D scenes. Unfortunately, +scenes are not worlds, let alone universes. If one ventures even a +hundred miles away from the (0.0, 0.0, 0.0) origin using only +single-precision floating-point coordinates, representable points +become quite quantized, to at very best a third of an inch (and much +more coarsely than that in practice). +

To "shrink" down to a small size (say the size of an IC transistor), +even very near (0.0, 0.0, 0.0), the same problem arises. +

+

If a large contiguous virtual universe is to be supported, some form +of +higher-resolution addressing is required. Thus the choice of 256-bit +positional components for "high-resolution" positions. +

+

+

+

Java 3D High-Resolution +Coordinates

+Java 3D high-resolution coordinates consist of three 256-bit +fixed-point numbers, one each for x, y, and z. +The fixed point is at bit 128, and the value 1.0 is defined to be +exactly 1 meter. This coordinate system is sufficient to describe a +universe in excess of several hundred billion light years across, yet +still define objects smaller than a proton (down to below the planck +length). Table +1 shows how many bits are needed above or below the fixed point +to represent the range of interesting physical dimensions. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1 – +Java 3D High-Resolution Coordinates
2n Meters Units
87.29Universe (20 billion light years) 
+
69.68Galaxy (100,000 light years)
53.07Light year
43.43Solar system diameter
23.60Earth diameter
10.65Mile
9.97Kilometer
0.00Meter
-19.93Micron
-33.22Angstrom
-115.57Planck length
+

+

A 256-bit fixed-point number also has the advantage of being able to +directly represent nearly any reasonable single-precision +floating-point value exactly. +

+

High-resolution coordinates in Java 3D are used only to embed more +traditional floating point coordinate systems within a much +higher-resolution substrate. In this way a visually seamless virtual +universe of any conceivable size or scale can be created, without worry +about numerical accuracy. +

+

+

+

Java 3D Virtual World +Coordinates

+Within a given virtual world coordinate system, positions are expressed +by three floating point numbers. The virtual world coordinate scale is +in meters, but this can be affected by scale changes in the object +hierarchy. +

Details of High-Resolution +Coordinates

+High-resolution coordinates are represented as signed, +two's-complement, fixed-point numbers consisting of 256 bits. Although +Java 3D keeps the internal representation of high-resolution +coordinates opaque, users specify such coordinates using 8-element +integer arrays. Java 3D treats the integer found at index 0 as +containing the most significant bits and the integer found at index 7 +as containing the least significant bits of the high-resolution +coordinate. The binary point is located at bit position 128, or between +the integers at index 3 and 4. A high-resolution coordinate of 1.0 is 1 +meter. +

The semantics of how file loaders deal with high-resolution +coordinates +is up to the individual file loader, as Java 3D does not directly +define any file-loading semantics. However, some general advice can be +given (note that this advice is not officially part of the +Java 3D specification). +

+

For "small" virtual universes (on the order of hundreds of meters +across in relative scale), a single Locale with high-resolution +coordinates at location (0.0, 0.0, 0.0) as the root node (below the +VirtualUniverse object) is sufficient; a loader can automatically +construct this node during the loading process, and the point in +high-resolution coordinates does not need any direct representation in +the external file. +

+

Larger virtual universes are expected to be constructed usually like +computer directory hierarchies, that is, as a "root" virtual universe +containing mostly external file references to embedded virtual +universes. In this case, the file reference object (user-specific data +hung off a Java 3D group or hi-res node) defines the location for the +data to be read into the current virtual universe. +

+

The data file's contents should be parented to the file object node +while being read, thus inheriting the high-resolution coordinates of +the file object as the new relative virtual universe origin of the +embedded scene graph. If this scene graph itself contains +high-resolution coordinates, it will need to be offset (translated) by +the amount in the file object's high-resolution coordinates and then +added to the larger virtual universe as new high-resolution +coordinates, with their contents hung off below them. Once again, this +procedure is not part of the official Java 3D specification, but some +more details on the care and use of high-resolution coordinates in +external file formats will probably be available as a Java 3D +application note. +

+

Authoring tools that directly support high-resolution coordinates +should create additional high-resolution coordinates as a user creates +new geometry "sufficiently" far away (or of different scale) from +existing high-resolution coordinates. +

+

Semantics of widely moving objects. Most fixed and +nearly-fixed objects stay attached to the same high-resolution Locale. +Objects that make wide changes in position or scale may periodically +need to be reparented to a more appropriate high-resolution Locale. If +no appropriate high-resolution Locale exists, the application may need +to create a new one. +

+

Semantics of viewing. The ViewPlatform object and +the +associated nodes in its hierarchy are very often widely moving objects. +Applications will typically attach the view platform to the most +appropriate high-resolution Locale. For display, all objects will first +have their positions translated by the difference between the location +of their high-resolution Locale and the view platform's high-resolution +Locale. (In the common case of the Locales being the same, no +translation is necessary.) +

+ + diff --git a/src/javax/media/j3d/doc-files/intro.gif b/src/javax/media/j3d/doc-files/intro.gif new file mode 100644 index 0000000..503f818 Binary files /dev/null and b/src/javax/media/j3d/doc-files/intro.gif differ diff --git a/src/javax/media/j3d/doc-files/intro.html b/src/javax/media/j3d/doc-files/intro.html new file mode 100644 index 0000000..f5ea134 --- /dev/null +++ b/src/javax/media/j3d/doc-files/intro.html @@ -0,0 +1,337 @@ + + + + + The Java 3D API - Introduction + + +

Disclaimer

+

+This guide, which contains documentation formerly +published separately from the javadoc-generated API documentation, +is not an +official API specification. This documentation may contain references to +Java and Java 3D, both of which are trademarks of Sun Microsystems, Inc. +Any reference to these and other trademarks of Sun Microsystems are +for explanatory purposes only. Their use does impart any rights beyond +those listed in the source code license. In particular, Sun Microsystems +retains all intellectual property and trademark rights as described in +the proprietary rights notice in the COPYRIGHT.txt file. + +

+
+

Introduction to the Java 3D API

+

The Java 3D API is an application +programming interface used for writing three-dimensional graphics +applications and applets. It gives developers high-level constructs for +creating and manipulating 3D geometry and for constructing the +structures used in rendering that geometry. Application developers can +describe very large virtual worlds using these constructs, which +provide Java 3D with enough information to render these worlds +efficiently. +

+

Java 3D delivers Java's "write once, run anywhere" +benefit to +developers of 3D graphics applications. Java 3D is part of the +JavaMedia suite of APIs, making it available on a wide range of +platforms. It also integrates well with the Internet because +applications and applets written using the Java 3D API have access to +the entire set of Java classes. +

+

The Java 3D API draws its ideas from existing +graphics APIs and from +new technologies. Java 3D's low-level graphics constructs synthesize +the best ideas found in low-level APIs such as Direct3D, OpenGL, +QuickDraw3D, and XGL. Similarly, its higher-level constructs synthesize +the best ideas found in several scene graph-based systems. Java 3D +introduces some concepts not commonly considered part of the graphics +environment, such as 3D spatial sound. Java 3D's sound capabilities +help to provide a more immersive experience for the user.
+

+ +

+

+

Programming Paradigm

+Java 3D is an object-oriented API. Applications construct individual +graphics elements as separate objects and connect them together into a +treelike structure called a scene graph. The application +manipulates these objects using their predefined accessor, mutator, and +node-linking methods. +

The Scene Graph Programming +Model

+Java 3D's scene graph-based programming model provides a simple and +flexible mechanism for representing and rendering scenes. The scene +graph contains a complete description of the entire scene, or virtual +universe. This includes the geometric data, the attribute information, +and the viewing information needed to render the scene from a +particular point of view. The "Scene +Graph Basics" document provides more information on the Java 3D +scene graph programming model. +

The Java 3D API improves on previous graphics APIs +by eliminating many +of the bookkeeping and programming chores that those APIs impose. Java +3D allows the programmer to think about geometric objects rather than +about triangles-about the scene and its composition rather than about +how to write the rendering code for efficiently displaying the scene. +

+

+

+

Rendering Modes

+Java 3D includes three different rendering modes: immediate mode, +retained mode, and compiled-retained mode (see "Execution +and Rendering Model"). +Each successive rendering mode allows Java 3D more freedom in +optimizing an application's execution. Most Java 3D applications will +want to take advantage of the convenience and performance benefits that +the retained and compiled-retained modes provide. +

Immediate Mode

+Immediate mode leaves little room for global +optimization at the scene graph level. Even so, Java 3D has raised the +level of abstraction and accelerates immediate mode rendering on a +per-object basis. An application must provide a Java 3D draw method +with a complete set of points, lines, or triangles, which are then +rendered by the high-speed Java 3D renderer. Of course, the application +can build these lists of points, lines, or triangles in any manner it +chooses. +

Retained Mode

+Retained mode requires an application to construct a scene graph and +specify which elements of that scene graph may change during rendering. +The scene graph describes the objects in the virtual universe, the +arrangement of those objects, and how the application animates those +objects. +

Compiled-Retained Mode

+Compiled-retained mode, like retained mode, requires the application to +construct a scene graph and specify which elements of the scene graph +may change during rendering. Additionally, the application can compile +some or all of the subgraphs that make up a complete scene graph. Java +3D compiles these graphs into an internal format. The compiled +representation of the scene graph may bear little resemblance to the +original tree structure provided by the application, however, it is +functionally equivalent. Compiled-retained mode provides the highest +performance. +

Extensibility

+Most Java 3D classes expose only accessor and mutator methods. Those +methods operate only on that object's internal state, making it +meaningless for an application to override them. Therefore, Java 3D +does not provide the capability to override the behavior of Java 3D +attributes. To make Java 3D work correctly, applications must call "super.setXxxxx" +for any attribute state set method that is overridden. +

Applications can extend Java 3D's classes and add +their own methods. +However, they may not override Java 3D's scene graph traversal +semantics because the nodes do not contain explicit traversal and draw +methods. Java 3D's renderer retains those semantics internally. +

+

Java 3D does provide hooks for mixing +Java 3D-controlled scene graph rendering and user-controlled rendering +using Java 3D's immediate mode constructs (see "Mixed-Mode Rendering"). Alternatively, +the application can +stop Java 3D's renderer and do all its drawing in immediate mode (see "Pure Immediate-Mode Rendering"). +

+

Behaviors require applications to extend the +Behavior object and to +override its methods with user-written Java code. These extended +objects should contain references to those scene graph objects that +they will manipulate at run time. The "Behaviors +and Interpolators" document describes Java 3D's behavior +model. +

+

+

+

High Performance

+Java 3D's programming model allows the Java 3D API to do the mundane +tasks, such as scene graph traversal, managing attribute state changes, +and so forth, thereby simplifying the application's job. Java 3D does +this without sacrificing performance. At first glance, it might appear +that this approach would create more work for the API; however, it +actually has the opposite effect. Java 3D's higher level of abstraction +changes not only the amount but, more important, also the kind of work +the API must perform. Java 3D does not need to impose the same type of +constraints as do APIs with a lower level of abstraction, thus allowing +Java 3D to introduce optimizations not possible with these lower-level +APIs. +

Additionally, leaving the details of rendering to +Java 3D allows it to +tune the rendering to the underlying hardware. For example, relaxing +the strict rendering order imposed by other APIs allows parallel +traversal as well as parallel rendering. Knowing which portions of the +scene graph cannot be modified at run time allows Java 3D to flatten +the tree, pretransform geometry, or represent the geometry in a native +hardware format without the need to keep the original data. +

+

+

+

Layered Implementation

+Besides optimizations at the scene graph level, one of the more +important factors that determines the performance of Java 3D is the +time it takes to render the visible geometry. Java 3D implementations +are layered to take advantage of the native, low-level API that is +available on a given system. In particular, Java 3D implementations +that use Direct3D and OpenGL are available. This means that Java 3D +rendering will be accelerated across the same wide range of systems +that are supported by these lower-level APIs. +

Target Hardware Platforms

+Java 3D is aimed at a wide range of 3D-capable hardware and software +platforms, from low-cost PC game cards and software renderers at the +low end, through midrange workstations, all the way up to very +high-performance specialized 3D image generators. +

Java 3D implementations are expected to provide +useful rendering rates +on most modern PCs, especially those with 3D graphics accelerator +cards. On midrange workstations, Java 3D is expected to provide +applications with nearly full-speed hardware performance. +

+

Finally, Java 3D is designed to scale as the +underlying hardware +platforms increase in speed over time. Tomorrow's 3D PC game +accelerators will support more complex virtual worlds than high-priced +workstations of a few years ago. Java 3D is prepared to meet this +increase in hardware performance. +

+

+

+

Structuring the Java 3D Program

+

This section illustrates how a developer might +structure a Java 3D application. The simple application in this example +creates a scene graph that draws an object in the middle of a window +and rotates the object about its center point. +

+

Java 3D Application Scene +Graph

+

The scene graph for the sample application is shown below. +

+

The scene graph consists of superstructure +components—a VirtualUniverse +object and a Locale object—and a set of branch graphs. Each branch +graph is a subgraph that is rooted by a BranchGroup node that is +attached to the superstructure. For more information, see "Scene Graph Basics." +

+

Application
+scene graph

+

+

+
    + Figure 1 – Application Scene Graph +
+

+A VirtualUniverse object defines a named universe. Java 3D permits the +creation of more than one universe, though the vast majority of +applications will use just one. The VirtualUniverse object provides a +grounding for scene graphs. All Java 3D scene graphs must connect to a +VirtualUniverse object to be displayed. For more information, see "Scene Graph Superstructure." +

+

Below the VirtualUniverse object is a Locale object. +The Locale object +defines the origin, in high-resolution coordinates, of its attached +branch graphs. A virtual universe may contain as many Locales as +needed. In this example, a single Locale object is defined with its +origin at (0.0, 0.0, 0.0). +

+

The scene graph itself starts with the BranchGroup +nodes. +A BranchGroup serves as the root of a +subgraph, called a branch graph, of the scene graph. Only +BranchGroup objects can attach to Locale objects. +

+

In this example there are two branch graphs and, +thus, two BranchGroup +nodes. Attached to the left BranchGroup are two subgraphs. One subgraph +consists of a user-extended Behavior leaf node. The Behavior node +contains Java code for manipulating the transformation matrix +associated with the object's geometry. +

+

The other subgraph in this BranchGroup consists of a +TransformGroup +node that specifies the position (relative to the Locale), orientation, +and scale of the geometric objects in the virtual universe. A single +child, a Shape3D leaf node, refers to two component objects: a Geometry +object and an Appearance object. The Geometry object describes the +geometric shape of a 3D object (a cube in our simple example). The +Appearance object describes the appearance of the geometry (color, +texture, material reflection characteristics, and so forth). +

+

The right BranchGroup has a single subgraph that +consists of a +TransformGroup node and a ViewPlatform leaf node. The TransformGroup +specifies the position (relative to the Locale), orientation, and scale +of the ViewPlatform. This transformed ViewPlatform object defines the +end user's view within the virtual universe. +

+

Finally, the ViewPlatform is referenced by a View +object that specifies +all of the parameters needed to render the scene from the point of view +of the ViewPlatform. Also referenced by the View object are other +objects that contain information, such as the drawing canvas into which +Java 3D renders, the screen that contains the canvas, and information +about the physical environment. +

+

+

+

Recipe for a Java 3D Program

+

The following steps are taken by the example program to create the +scene graph elements and link them together. Java 3D will then render +the scene graph and display the graphics in a window on the screen:

+
    +1. Create a Canvas3D object and add it to the Applet panel. +

    2. Create a BranchGroup as the root of the scene branch graph.

    +

    3. Construct a Shape3D node with a TransformGroup node above it.

    +

    4. Attach a RotationInterpolator behavior to the TransformGroup.

    +

    5. Call the simple universe utility function to do the following:

    +
      +a. Establish a virtual universe with a single high-resolution Locale +(see "Scene Graph Basics"). +

      b. Create the PhysicalBody, PhysicalEnvironment, View, and +ViewPlat-form objects.

      +

      c. Create a BranchGroup as the root of the view platform branch +graph.

      +

      d. Insert the view platform branch graph into the Locale.

      +
    +6. Insert the scene branch graph into the simple universe's Locale. +
+

The Java 3D renderer then starts running in an infinite loop. The +renderer conceptually performs the following operations:

+
    while(true) {
Process input
If (request to exit) break
Perform Behaviors
Traverse the scene graph and render visible objects
}
Cleanup and exit
+

HelloUniverse: A Sample Java +3D Program

+

Click here to see code fragments +from a simple program, HelloUniverse.java, +that creates a cube and a RotationInterpolator behavior object that +rotates the cube at a constant rate of pi/2 radians per second.
+

+

Other Documents
+

+

Here are other documents that provide explanatory material, +previously included as part of +the Java 3D API Specification Guide.
+

+ +


+

+ + diff --git a/src/javax/media/j3d/package.html b/src/javax/media/j3d/package.html new file mode 100644 index 0000000..a9a3553 --- /dev/null +++ b/src/javax/media/j3d/package.html @@ -0,0 +1,40 @@ + + + + + javax.media.j3d + + + +

Provides the core set of classes for the +3D graphics API for the Java platform; click here for more information, +including explanatory material that was formerly found in the guide. +

+ +

The 3D API is an application +programming interface used for writing three-dimensional graphics +applications and applets. It gives developers high-level constructs for +creating and manipulating 3D geometry and for constructing the +structures used in rendering that geometry. Application developers can +describe very large virtual worlds using these constructs, which +provide the runtime system with enough information to render these worlds +efficiently. +

+ + + + + -- cgit v1.2.3